找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 43|回复: 0

[客户端] 添加 ImGui 老外分享

[复制链接]

38

主题

15

回帖

218

积分

管理员

积分
218
发表于 2025-6-24 14:16:07 | 显示全部楼层 |阅读模式
原帖地址: https://forum.playkuro.com/showthread.php?tid=28&pid=32



OVERVIEW - D3D8 TO D3D9 CONVERSION WITH IMGUI

This guide explains how to integrate Dear ImGui into legacy Direct3D 8 games by using a D3D8 to D3D9 wrapper. This technique allows modern UI overlays in games that still use the deprecated D3D8 API.

Useful Resources:
d3d8to9 by crosire - The original D3D8 to D3D9 wrapper
Dear ImGui - Immediate mode GUI library
Microsoft Detours - API hooking library


ARCHITECTURE OVERVIEW

The integration consists of 4 main components:
D3D8to9 Wrapper - Translates D3D8 API calls to D3D9
API Hooking Layer - Intercepts Direct3D creation and device methods
ImGui Integration - Renders UI using the wrapped D3D9 device
Input Handling - Captures mouse/keyboard for UI interaction


STEP 1: D3D8TO9 WRAPPER SETUP

Required Files:
d3d8to9.cpp - Main wrapper entry point
d3d8to9.hpp - Interface definitions
d3d8to9_device.cpp - Device implementation
d3d8types.hpp - Type conversions
Additional implementation files for resources

Key Components:

// d3d8to9.cpp - Export the Direct3DCreate8 function
extern "C" IDirect3D8* WINAPI Direct3DCreate8(UINT SDKVersion)
{
    // Load d3dx9_43.dll for shader compilation
    LoadLibrary(TEXT("d3dx9_43.dll"));
   
    // Create D3D9 interface
    IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
    if (!d3d9) return nullptr;
   
    // Wrap in D3D8 interface
    return new Direct3D8(d3d9);
}

// d3d8to9.hpp - Wrapper class structure
class Direct3D8 : public IDirect3D8
{
private:
    IDirect3D9* ProxyInterface;
    ULONG ReferenceCount;

public:
    Direct3D8(IDirect3D9* pDirect3D9)
        : ProxyInterface(pDirect3D9), ReferenceCount(1) {}
   
    // IUnknown methods
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObj);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();
   
    // IDirect3D8 methods (translate all calls to D3D9)
    virtual HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction);
    virtual UINT STDMETHODCALLTYPE GetAdapterCount();
    virtual HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER8* pIdentifier);
    virtual UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter);
    virtual HRESULT STDMETHODCALLTYPE EnumAdapterModes(UINT Adapter, UINT Mode, D3DDISPLAYMODE* pMode);
    virtual HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode);
    virtual HRESULT STDMETHODCALLTYPE CheckDeviceType(UINT Adapter, D3DDEVTYPE CheckType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL Windowed);
    virtual HRESULT STDMETHODCALLTYPE CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat);
    virtual HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType);
    virtual HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat);
    virtual HRESULT STDMETHODCALLTYPE GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS8* pCaps);
    virtual HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter);
    virtual HRESULT STDMETHODCALLTYPE CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS8* pPresentationParameters, IDirect3DDevice8** ppReturnedDeviceInterface);
};

// Example of parameter conversion in CreateDevice
HRESULT Direct3D8::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS8* pPresentationParameters, IDirect3DDevice8** ppReturnedDeviceInterface)
{
    // Convert D3DPRESENT_PARAMETERS8 to D3DPRESENT_PARAMETERS9
    D3DPRESENT_PARAMETERS PresentParams;
    PresentParams.BackBufferWidth = pPresentationParameters->BackBufferWidth;
    PresentParams.BackBufferHeight = pPresentationParameters->BackBufferHeight;
    PresentParams.BackBufferFormat = pPresentationParameters->BackBufferFormat;
    PresentParams.BackBufferCount = pPresentationParameters->BackBufferCount;
    PresentParams.MultiSampleType = pPresentationParameters->MultiSampleType;
    PresentParams.MultiSampleQuality = 0; // New in D3D9
    PresentParams.SwapEffect = pPresentationParameters->SwapEffect;
    PresentParams.hDeviceWindow = pPresentationParameters->hDeviceWindow;
    PresentParams.Windowed = pPresentationParameters->Windowed;
    PresentParams.EnableAutoDepthStencil = pPresentationParameters->EnableAutoDepthStencil;
    PresentParams.AutoDepthStencilFormat = pPresentationParameters->AutoDepthStencilFormat;
    PresentParams.Flags = pPresentationParameters->Flags;
    PresentParams.FullScreen_RefreshRateInHz = pPresentationParameters->FullScreen_RefreshRateInHz;
    PresentParams.PresentationInterval = pPresentationParameters->FullScreen_PresentationInterval;

    IDirect3DDevice9* DeviceInterface;
    HRESULT hr = ProxyInterface->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, &PresentParams, &DeviceInterface);
   
    if (SUCCEEDED(hr))
    {
        *ppReturnedDeviceInterface = new Direct3DDevice8(DeviceInterface);
        // Copy back any changes
        pPresentationParameters->BackBufferWidth = PresentParams.BackBufferWidth;
        pPresentationParameters->BackBufferHeight = PresentParams.BackBufferHeight;
        pPresentationParameters->BackBufferFormat = PresentParams.BackBufferFormat;
        pPresentationParameters->FullScreen_RefreshRateInHz = PresentParams.FullScreen_RefreshRateInHz;
    }
   
    return hr;
}


STEP 2: HOOKING INFRASTRUCTURE

Create D3D8to9Hook.cpp:

#include <windows.h>
#include <d3d8.h>
#include <detours.h>

// Function pointer for original Direct3DCreate8
typedef IDirect3D8* (WINAPI* Direct3DCreate8_t)(UINT SDKVersion);
Direct3DCreate8_t pOriginalDirect3DCreate8 = nullptr;

class D3D8to9Hook {
public:
    static void Initialize() {
        // Get the original Direct3DCreate8 function
        HMODULE d3d8Module = GetModuleHandle(L"d3d8.dll");
        if (!d3d8Module) {
            // If not loaded, load it
            d3d8Module = LoadLibrary(L"d3d8.dll");
        }
      
        if (d3d8Module) {
            pOriginalDirect3DCreate8 = (Direct3DCreate8_t)GetProcAddress(d3d8Module, "Direct3DCreate8");
           
            if (pOriginalDirect3DCreate8) {
                // Install hook using Detours
                DetourTransactionBegin();
                DetourUpdateThread(GetCurrentThread());
                DetourAttach(&(PVOID&)pOriginalDirect3DCreate8, Direct3DCreate8_Hook);
                LONG result = DetourTransactionCommit();
               
                if (result == NO_ERROR) {
                    OutputDebugStringA("D3D8to9Hook: Successfully hooked Direct3DCreate8\n");
                } else {
                    OutputDebugStringA("D3D8to9Hook: Failed to hook Direct3DCreate8\n");
                }
            }
        }
    }
   
    static void Shutdown() {
        if (pOriginalDirect3DCreate8) {
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourDetach(&(PVOID&)pOriginalDirect3DCreate8, Direct3DCreate8_Hook);
            DetourTransactionCommit();
        }
    }
   
private:
    static IDirect3D8* WINAPI Direct3DCreate8_Hook(UINT SDKVersion) {
        OutputDebugStringA("D3D8to9Hook: Direct3DCreate8 called - redirecting to d3d8to9 wrapper\n");
      
        // Call our d3d8to9 wrapper instead of original
        return :: (Just remove this, this creates this emoji Big Grin)Direct3DCreate8(SDKVersion);  // This calls our wrapper function
    }
};

// Alternative approach: Hook by replacing the export in the DLL
// This is useful if you're creating a replacement d3d8.dll
extern "C" {
    __declspec(dllexport) IDirect3D8* WINAPI Direct3DCreate8(UINT SDKVersion) {
        // This function will be called instead of the original
        // Initialize d3d8to9 wrapper here
        return CreateD3D8to9Wrapper(SDKVersion);
    }
}

Alternative: DLL Replacement Method

If you prefer to replace d3d8.dll entirely, create a module definition file:

; d3d8.def - Module definition for d3d8.dll replacement
LIBRARY d3d8
EXPORTS
    Direct3DCreate8 @1 NONAME
    DebugSetMute @2 NONAME
    ValidatePixelShader @3 NONAME
    ValidateVertexShader @4 NONAME

Then in your DLL project settings, reference this .def file to ensure proper exports.


STEP 3: IMGUI D3D9 HOOK SETUP

Create SimpleD3D9ImGuiHook.cpp:

#include <windows.h>
#include <d3d9.h>
#include <detours.h>
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx9.h"

// Function pointers for original D3D9 methods
typedef HRESULT(WINAPI* EndScene_t)(LPDIRECT3DDEVICE9);
typedef HRESULT(WINAPI* Reset_t)(LPDIRECT3DDEVICE9, D3DPRESENT_PARAMETERS*);

EndScene_t oEndScene = nullptr;
Reset_t oReset = nullptr;

// Global variables
static LPDIRECT3DDEVICE9 g_pd3dDevice = nullptr;
static bool g_ImGuiInitialized = false;
static HWND g_GameWindow = nullptr;

// Forward declarations
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc_Hook(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC oWndProc = nullptr;

HWND FindGameWindow() {
    // Try common game window names - adjust for your specific game
    HWND hwnd = FindWindow(NULL, L"MapleStory");
    if (!hwnd) hwnd = FindWindow(NULL, L"MapleStory Client");
    if (!hwnd) hwnd = FindWindow(L"MapleStoryClass", NULL);
   
    // If still not found, try to find the active window
    if (!hwnd) hwnd = GetForegroundWindow();
   
    return hwnd;
}

// Hook EndScene to render ImGui
HRESULT WINAPI EndScene_Hook(LPDIRECT3DDEVICE9 pDevice) {
    if (!g_ImGuiInitialized && pDevice) {
        g_pd3dDevice = pDevice;
        g_GameWindow = FindGameWindow();
      
        if (g_GameWindow) {
            // Initialize ImGui
            IMGUI_CHECKVERSION();
            ImGui::CreateContext();
            ImGuiIO& io = ImGui::GetIO();
            io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
           
            // Setup ImGui style
            ImGui::StyleColorsDark();
           
            // Setup Platform/Renderer backends
            ImGui_ImplWin32_Init(g_GameWindow);
            ImGui_ImplDX9_Init(pDevice);
           
            // Hook window procedure for input
            oWndProc = (WNDPROC)SetWindowLongPtr(g_GameWindow, GWLP_WNDPROC, (LONG_PTR)WndProc_Hook);
           
            g_ImGuiInitialized = true;
            OutputDebugStringA("ImGui initialized successfully\n");
        }
    }
   
    if (g_ImGuiInitialized && pDevice == g_pd3dDevice) {
        // Start the Dear ImGui frame
        ImGui_ImplDX9_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();
      
        // Your UI code here - example overlay
        static bool show_demo = false;
        static bool show_overlay = true;
      
        if (show_overlay) {
            ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_FirstUseEver);
            ImGui::SetNextWindowSize(ImVec2(300, 200), ImGuiCond_FirstUseEver);
           
            if (ImGui::Begin("Game Overlay", &show_overlay)) {
                ImGui::Text("Hello from ImGui!");
                ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
                          1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
               
                if (ImGui::Button("Show Demo Window")) {
                    show_demo = !show_demo;
                }
               
                if (ImGui::CollapsingHeader("Game Info")) {
                    ImGui::Text("Device: 0x%p", pDevice);
                    ImGui::Text("Window: 0x%p", g_GameWindow);
                }
            }
            ImGui::End();
        }
      
        if (show_demo) {
            ImGui::ShowDemoWindow(&show_demo);
        }
      
        // Rendering
        ImGui::EndFrame();
        ImGui::Render();
        ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
    }
   
    // Call original EndScene
    return oEndScene(pDevice);
}

// Handle device reset
HRESULT WINAPI Reset_Hook(LPDIRECT3DDEVICE9 pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters) {
    if (g_ImGuiInitialized) {
        ImGui_ImplDX9_InvalidateDeviceObjects();
    }
   
    HRESULT result = oReset(pDevice, pPresentationParameters);
   
    if (g_ImGuiInitialized && SUCCEEDED(result)) {
        ImGui_ImplDX9_CreateDeviceObjects();
    }
   
    return result;
}

// Window procedure hook for input handling
LRESULT WINAPI WndProc_Hook(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (g_ImGuiInitialized) {
        // Let ImGui handle the message first
        if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
            return true;
      
        // Check if ImGui wants to capture input
        ImGuiIO& io = ImGui::GetIO();
        switch (msg) {
        case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
        case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
        case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
        case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
        case WM_XBUTTONUP:
        case WM_MOUSEWHEEL:
        case WM_MOUSEHWHEEL:
            if (io.WantCaptureMouse)
                return true;
            break;
        case WM_KEYDOWN:
        case WM_KEYUP:
        case WM_SYSKEYDOWN:
        case WM_SYSKEYUP:
        case WM_CHAR:
            if (io.WantCaptureKeyboard)
                return true;
            break;
        }
    }
   
    // Call original window procedure
    return CallWindowProc(oWndProc, hWnd, msg, wParam, lParam);
}

// Install hooks by creating temporary device to get vtable
void InstallD3D9Hooks() {
    OutputDebugStringA("Installing D3D9 hooks...\n");
   
    // Create temporary window class
    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = DefWindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = L"TempD3D9Window";
    RegisterClassEx(&wc);
   
    // Create temporary window
    HWND hwnd = CreateWindow(L"TempD3D9Window", L"", WS_OVERLAPPEDWINDOW,
                            0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), NULL);
   
    if (hwnd) {
        // Create D3D9 interface and device
        LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);
        if (pD3D) {
            D3DPRESENT_PARAMETERS d3dpp = {};
            d3dpp.Windowed = TRUE;
            d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
            d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
            d3dpp.hDeviceWindow = hwnd;
           
            LPDIRECT3DDEVICE9 pd3dDevice;
            HRESULT hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
                                          D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT,
                                          &d3dpp, &pd3dDevice);
           
            if (SUCCEEDED(hr)) {
                // Get vtable addresses
                void** vtable = *(void***)pd3dDevice;
                oEndScene = (EndScene_t)vtable[42];  // EndScene is at index 42
                oReset = (Reset_t)vtable[16];        // Reset is at index 16
               
                // Install hooks using Detours
                DetourTransactionBegin();
                DetourUpdateThread(GetCurrentThread());
                DetourAttach(&(PVOID&)oEndScene, EndScene_Hook);
                DetourAttach(&(PVOID&)oReset, Reset_Hook);
               
                if (DetourTransactionCommit() == NO_ERROR) {
                    OutputDebugStringA("D3D9 hooks installed successfully\n");
                } else {
                    OutputDebugStringA("Failed to install D3D9 hooks\n");
                }
               
                pd3dDevice->Release();
            }
            pD3D->Release();
        }
      
        DestroyWindow(hwnd);
    }
   
    UnregisterClass(L"TempD3D9Window", GetModuleHandle(NULL));
}

// Cleanup function
void CleanupImGuiHooks() {
    if (g_ImGuiInitialized) {
        if (oWndProc && g_GameWindow) {
            SetWindowLongPtr(g_GameWindow, GWLP_WNDPROC, (LONG_PTR)oWndProc);
        }
      
        ImGui_ImplDX9_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui:: (Just remove this, this creates this emoji Big Grin)DestroyContext();
      
        g_ImGuiInitialized = false;
    }
   
    // Remove hooks
    if (oEndScene) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)oEndScene, EndScene_Hook);
        DetourDetach(&(PVOID&)oReset, Reset_Hook);
        DetourTransactionCommit();
    }
}


STEP 4: INPUT HANDLING

Hook Window Procedure:

WNDPROC oWndProc;
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);

LRESULT WINAPI WndProc_Hook(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (g_ImGuiInitialized) {
        ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam);
      
        // Block game input when ImGui wants it
        ImGuiIO& io = ImGui::GetIO();
        if (io.WantCaptureMouse || io.WantCaptureKeyboard) {
            return true;
        }
    }
   
    return CallWindowProc(oWndProc, hWnd, msg, wParam, lParam);
}

void HookWindowProc() {
    HWND gameWindow = FindWindow(NULL, "MapleStory");
    oWndProc = (WNDPROC)SetWindowLongPtr(gameWindow, GWLP_WNDPROC, (LONG_PTR)WndProc_Hook);
}


STEP 5: DLL ENTRY POINT

dllmain.cpp:

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) {
    switch (reason) {
    case DLL_PROCESS_ATTACH:
        // Initialize in order
        D3D8to9Hook::Initialize();    // Hook Direct3DCreate8
        InstallD3D9Hooks();          // Hook D3D9 methods
        HookWindowProc();            // Hook input
        break;
      
    case DLL_PROCESS_DETACH:
        // Cleanup ImGui
        if (g_ImGuiInitialized) {
            ImGui_ImplDX9_Shutdown();
            ImGui_ImplWin32_Shutdown();
            ImGui:: (Just remove this, this creates this emoji Big Grin)DestroyContext();
        }
        break;
    }
    return TRUE;
}


REQUIRED DEPENDENCIES

GitHub Repositories:
Microsoft Detours - API hooking library
Dear ImGui - Immediate mode GUI library
d3d8to9 - D3D8 to D3D9 wrapper reference
DirectX Graphics Samples - Official DirectX examples

Required Files to Download:
Microsoft Detours:
  - detours.lib (from build or NuGet package)
  - detours.h, detver.h (headers)
Dear ImGui:
  - imgui.cpp, imgui.h (core)
  - imgui_demo.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp
  - imgui_impl_win32.cpp, imgui_impl_win32.h (Win32 backend)
  - imgui_impl_dx9.cpp, imgui_impl_dx9.h (DirectX 9 backend)
DirectX 9 SDK:
  - d3d9.lib, d3dx9.lib (link libraries)
  - d3d9.h, d3dx9.h (headers)

Runtime Requirements:
d3d9.dll - DirectX 9 runtime (Windows system)
d3dx9_43.dll - D3DX9 helper library (download)
Your compiled DLL with d3d8.dll exports

NuGet Packages (Visual Studio):
<!-- Add to your .vcxproj or use Package Manager -->
<PackageReference Include="Microsoft.Detours" Version="4.0.1" />
<PackageReference Include="directxtk" Version="2024.2.8.1" />


BUILD CONFIGURATION

Visual Studio Project Settings:

<!-- .vcxproj excerpt -->
<ItemDefinitionGroup>
  <ClCompile>
    <PreprocessorDefinitions>DIRECT3D_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
  </ClCompile>
  <Link>
    <AdditionalDependencies>d3d9.lib;d3dx9.lib;detours.lib;%(AdditionalDependencies)</AdditionalDependencies>
    <ModuleDefinitionFile>d3d8.def</ModuleDefinitionFile>
  </Link>
</ItemDefinitionGroup>

Module Definition File (d3d8.def):

LIBRARY d3d8
EXPORTS
    Direct3DCreate8 @1


COMMON ISSUES AND SOLUTIONS

Problem: Game crashes on startup
Check d3dx9_43.dll is present
Verify hook timing - some games need delayed initialization
Use SEH to catch exceptions during device creation

Problem: ImGui not rendering
Ensure EndScene is being called by the game
Check if device is using pure device flag
Verify state blocks are properly saved/restored

Problem: Input not working
Make sure window handle is correct
Check if game uses DirectInput (needs separate handling)
Verify WndProc hook is installed after window creation


ADVANCED FEATURES

Multi-threaded Rendering:
// Use critical section for thread safety
CRITICAL_SECTION g_cs;
InitializeCriticalSection(&g_cs);

HRESULT WINAPI EndScene_Hook(LPDIRECT3DDEVICE9 pDevice) {
    EnterCriticalSection(&g_cs);
    // ImGui rendering code
    LeaveCriticalSection(&g_cs);
}

State Management:
// Save D3D9 state before ImGui
IDirect3DStateBlock9* d3d9_state_block = NULL;
pDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block);
d3d9_state_block->Capture();

// Render ImGui
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());

// Restore state
d3d9_state_block->Apply();
d3d9_state_block->Release();


EXAMPLE PROJECT STRUCTURE

YourProject/
├── src/
│  ├── d3d8to9/
│  │  ├── d3d8to9.cpp
│  │  ├── d3d8to9.hpp
│  │  ├── d3d8to9_device.cpp
│  │  └── d3d8types.hpp
│  ├── hooks/
│  │  ├── D3D8to9Hook.cpp
│  │  └── SimpleD3D9ImGuiHook.cpp
│  ├── imgui/
│  │  ├── imgui.cpp
│  │  ├── imgui.h
│  │  ├── imgui_impl_win32.cpp
│  │  ├── imgui_impl_win32.h
│  │  ├── imgui_impl_dx9.cpp
│  │  └── imgui_impl_dx9.h
│  └── dllmain.cpp
├── lib/
│  ├── detours.lib
│  ├── d3d9.lib
│  └── d3dx9.lib
├── include/
│  ├── detours.h
│  ├── d3d9.h
│  └── d3dx9.h
├── d3d8.def
└── YourProject.vcxproj


DEBUGGING TIPS

Use OutputDebugString for logging:
// Add this to track initialization
OutputDebugStringA("D3D8to9: Direct3DCreate8 called\n");
OutputDebugStringA("ImGui: EndScene hook called\n");

// Use DebugView from Sysinternals to see output
// Download: https://docs.microsoft.com/en-us/sysinte.../debugview

Common vtable indices for D3D9:

// IDirect3DDevice9 vtable indices
// 0  - QueryInterface
// 1  - AddRef
// 2  - Release
// 16 - Reset
// 42 - EndScene
// 43 - Present

// Verify with:
void** vtable = *(void***)pDevice;
OutputDebugStringA("EndScene address: 0x%p\n", vtable[42]);

Useful GitHub Examples:
ImGui DirectX Hook Example
Kiero - Universal graphics hook

CONCLUSION

This architecture allows seamless integration of modern UI frameworks into legacy D3D8 applications. The game continues using D3D8 APIs while ImGui renders through the underlying D3D9 device, providing the best of both worlds - compatibility and modern features.

Key benefits:
No game code modification required
Modern UI capabilities in legacy games
Transparent D3D8 to D3D9 conversion
Full ImGui feature set available
Minimal performance impact

Remember to handle edge cases specific to your target game, test thoroughly, and always backup original game files!

Guide created based on Ezorsia V2 MapleStory V83 client implementation

有问题欢迎跟帖提问。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|小黑屋|蘑菇物语

GMT+8, 2025-7-2 06:36 , Processed in 0.059954 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表