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'sPresentfunction 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 theIDXGISwapChain::Presentfunction signature. Ensures our hook function matches the original function's parameters and return type.oPresent: Pointer to the originalPresentfunction. 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.ImGuiIOconfiguration: 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 (useFindWindowor 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.Presentis 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/Endblock creates a window, andText/SliderFloatare 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 originalPresentfunction 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 hookingD3D11CreateDeviceAndSwapChainor 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
Presentfunction (index 8, 0-based). MH_CreateHook: Creates the hook, replacing the originalPresentwith ourhkPresent.MH_EnableHook: Activates the hook, so the game starts calling ourhkPresentinstead 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
D3D11CreateDeviceAndSwapChainto 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 viaGetForegroundWindow()(for testing) orFindWindowwith the game's class name/title. - Cleanup: When shutting down, call
ImGui_ImplDX11_Shutdown(),ImGui_ImplWin32_Shutdown(),ImGui::DestroyContext(),MH_DisableHook(), andMH_Uninitialize()to avoid memory leaks. - Thread Safety: The
Presentfunction 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




