COMPLETE D3D8 TO D3D9 WRAPPER GUIDE - ADDING IMGUI TO LEGACY GAMES
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:
ARCHITECTURE OVERVIEW
The integration consists of 4 main components:
STEP 1: D3D8TO9 WRAPPER SETUP
Required Files:
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
)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
)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
)DestroyContext();
}
break;
}
return TRUE;
}
REQUIRED DEPENDENCIES
GitHub Repositories:
Required Files to Download:
Runtime Requirements:
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
Problem: ImGui not rendering
Problem: Input not working
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:
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:
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
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

}
};
// 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

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

}
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:
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