You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

DirectX 11 + ImGui钩子工作原理探究:求最简DX11游戏ImGui悬浮层实现代码及逐行功能说明

Got it, let's cut through the noise and give you the absolute minimal code for a DirectX 11 ImGui overlay, plus a detailed breakdown of every part so you understand exactly what's necessary and why. I've stripped out all the extra fluff you'll see in bloated repos—only the core logic here.

Absolute Minimal DirectX 11 ImGui Overlay Code
#include <d3d11.h>
#include <imgui.h>
#include <imgui_impl_dx11.h>
#include <imgui_impl_win32.h>
#include <MinHook.h>

// Global references we need across the overlay
ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
typedef HRESULT(WINAPI* Present_t)(IDXGISwapChain*, UINT, UINT);
Present_t oPresent = nullptr;

// Initialize ImGui with DirectX 11 and Win32 backends
void InitImGui(IDXGISwapChain* pSwapChain) {
    // Grab DirectX device and context from the game's swap chain
    pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pd3dDevice);
    g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);

    // Set up ImGui core context
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; // Keep game cursor visible

    // Attach ImGui to the game's window and DirectX device
    ImGui_ImplWin32_Init(FindWindowA(NULL, "Game Window Title")); // Replace with actual game window title
    ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
}

// Our hooked version of the Present function (runs every frame)
HRESULT WINAPI hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) {
    // Initialize ImGui only once (Present is called every frame)
    static bool isImGuiInitialized = false;
    if (!isImGuiInitialized) {
        InitImGui(pSwapChain);
        isImGuiInitialized = true;
    }

    // Start a new ImGui frame (required before drawing any UI)
    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    // -------------------- Your overlay content goes here --------------------
    ImGui::Begin("Simple DX11 Overlay");
    ImGui::Text("Hello from ImGui!");
    float someFloatVar = 50.0f;
    ImGui::SliderFloat("Example Slider", &someFloatVar, 0.0f, 100.0f);
    ImGui::End();
    // -----------------------------------------------------------------------

    // Render ImGui to the screen
    ImGui::Render();
    g_pd3dDeviceContext->OMSetRenderTargets(1, nullptr, nullptr); // Unbind game's render targets temporarily
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

    // Call the original Present function so the game renders normally
    return oPresent(pSwapChain, SyncInterval, Flags);
}

// Set up the hook for the Present function
bool SetupOverlayHook() {
    // Initialize MinHook (our function hooking library)
    if (MH_Initialize() != MH_OK) {
        return false;
    }

    // Note: In real code, you'll need to get the game's swap chain pointer first
    // For example, by hooking D3D11CreateDeviceAndSwapChain or enumerating DXGI swap chains
    IDXGISwapChain* pSwapChain = nullptr; // Replace with actual swap chain pointer

    // Get the swap chain's virtual table (DX11 uses virtual functions for interfaces)
    void** vTable = *reinterpret_cast<void***>(pSwapChain);
    // Present is the 9th function in the IDXGISwapChain vtable (index 8, 0-based)
    const int PresentVTableIndex = 8;

    // Create and enable the hook
    if (MH_CreateHook(vTable[PresentVTableIndex], &hkPresent, reinterpret_cast<void**>(&oPresent)) != MH_OK) {
        return false;
    }
    if (MH_EnableHook(vTable[PresentVTableIndex]) != MH_OK) {
        return false;
    }

    return true;
}
Line-by-Line Breakdown

Let's go through each critical section to understand why it's necessary:

1. Header Includes

  • <d3d11.h>: Core DirectX 11 header—gives us access to devices, swap chains, and rendering functions. Required for interacting with the game's graphics pipeline.
  • <imgui.h>: ImGui's core header—defines all UI functions and context management. Required.
  • <imgui_impl_dx11.h> / <imgui_impl_win32.h>: ImGui's platform-specific backends. These translate ImGui's abstract UI commands into DirectX 11 render calls and handle Windows input (mouse/keyboard). Required to make ImGui work with DX11.
  • <MinHook.h>: Lightweight function hooking library. We use it to replace the game's Present function with our own. Required to inject our overlay logic into the game's render loop.

2. Global Variables

  • g_pd3dDevice / g_pd3dDeviceContext: DirectX 11 device and immediate context. ImGui needs these to create render resources and submit draw commands. We store them globally because we need access across the hook and ImGui initialization.
  • Present_t: Type definition for the IDXGISwapChain::Present function signature. Ensures our hook function matches the original function's parameters and return type.
  • oPresent: Pointer to the original Present function. We need to call this after rendering our overlay so the game can draw its own content.

3. InitImGui Function

  • pSwapChain->GetDevice(...): Extracts the DirectX device from the game's swap chain. The swap chain is the object the game uses to render frames to the window, so it's tied directly to the device.
  • g_pd3dDevice->GetImmediateContext(...): Gets the device's immediate context, which executes rendering commands in real-time.
  • ImGui::CreateContext(): Initializes ImGui's internal state and context. Must be called once before any ImGui UI is drawn.
  • ImGuiIO configuration: Disables ImGui's cursor change so the game's cursor stays visible (optional but useful for overlays).
  • ImGui_ImplWin32_Init(...): Attaches ImGui to the game's window. You must replace the window title with the actual game's window title (use FindWindow or get the handle via other means).
  • ImGui_ImplDX11_Init(...): Links ImGui to the game's DirectX device and context, so ImGui knows how to render to the game's window.

4. hkPresent (Hooked Present Function)

  • static bool isImGuiInitialized: Ensures we only initialize ImGui once. Present is called every frame, so we don't want to reinitialize ImGui 60+ times per second.
  • ImGui_*_NewFrame(): Resets ImGui's frame state, processes input (mouse/keyboard), and prepares for drawing UI. Must be called before any ImGui widgets.
  • ImGui UI code: The Begin/End block creates a window, and Text/SliderFloat are example widgets. Replace this with your own overlay content.
  • ImGui::Render(): Converts ImGui's UI commands into a draw data structure that can be rendered by DirectX.
  • OMSetRenderTargets: Temporarily unbinds the game's render targets to ensure ImGui's rendering doesn't interfere with the game's frame buffer.
  • ImGui_ImplDX11_RenderDrawData(...): Draws ImGui's UI using the DirectX 11 backend.
  • return oPresent(...): Calls the original Present function so the game can render its own frame. If you skip this, the game will be black.

5. SetupOverlayHook Function

  • MH_Initialize(): Initializes MinHook. Required before creating any hooks.
  • pSwapChain: In real code, you need to get the game's swap chain pointer. Common methods include hooking D3D11CreateDeviceAndSwapChain or enumerating DXGI swap chains via a temporary DirectX window.
  • Virtual table access: DX11 interfaces use virtual tables, so we get the swap chain's vtable and target the Present function (index 8, 0-based).
  • MH_CreateHook: Creates the hook, replacing the original Present with our hkPresent.
  • MH_EnableHook: Activates the hook, so the game starts calling our hkPresent instead of the original.
Key Notes for Real-World Use
  • Swap Chain Acquisition: The biggest missing piece in this minimal code is getting the game's swap chain pointer. You'll need to hook D3D11CreateDeviceAndSwapChain to capture it when the game initializes its graphics pipeline.
  • Window Handle: Replace FindWindowA(NULL, "Game Window Title") with the actual game window handle. You can get this via GetForegroundWindow() (for testing) or FindWindow with the game's class name/title.
  • Cleanup: When shutting down, call ImGui_ImplDX11_Shutdown(), ImGui_ImplWin32_Shutdown(), ImGui::DestroyContext(), MH_DisableHook(), and MH_Uninitialize() to avoid memory leaks.
  • Thread Safety: The Present function runs on the game's render thread. Ensure all ImGui operations happen on this thread (which they will, since our hook runs in the same thread).
  • Compilation: Link against d3d11.lib, dxgi.lib, MinHook.lib, and include ImGui's source files (or link against ImGui's compiled library) in your project.

内容的提问来源于stack exchange,提问作者cogitator

火山引擎 最新活动