41 #include <imgui_impl_dx11.h>
42 #include <imgui_impl_win32.h>
45 #pragma comment(lib, "d3d11.lib")
48 extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND s_hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
50 using IDXGISwapChainPresentPtr = std::add_pointer<HRESULT FMGUI_FASTCALL(IDXGISwapChain *pSwapChain, UINT syncInterval, UINT flags)>::type;
55 static LPVOID LookupSwapChainVTable(
void);
56 static std::string MinHookStatusToStdString(MH_STATUS mhStatus);
57 static HRESULT FMGUI_FASTCALL SwapChainPresentImpl(IDXGISwapChain *pSwapChain, UINT syncInterval, UINT flags);
58 static HRESULT GetDevice(IDXGISwapChain *
const pSwapChain, ID3D11Device **ppDevice);
59 static HRESULT GetDeviceContext(IDXGISwapChain *
const pSwapChain, ID3D11Device **ppDevice, ID3D11DeviceContext **ppDeviceContext);
60 static void OnResize(IDXGISwapChain *pSwapChain, UINT newWidth, UINT newHeight);
61 static LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
63 template <
typename Type>
64 inline void ReleaseCOM(Type *pInterface)
66 if (pInterface !=
nullptr)
69 pInterface->Release();
74 template <
typename Type>
75 inline void ReleaseCOM(Type **ppInterface)
77 if (*ppInterface !=
nullptr)
79 (*ppInterface)->Release();
80 (*ppInterface) =
nullptr;
84 static ID3D11Device *s_pDevice =
nullptr;
85 static ID3D11DeviceContext *s_pDeviceContext =
nullptr;
86 static ID3D11RenderTargetView *s_pRenderTargetView =
nullptr;
87 static constexpr LPCSTR kDXGI_MODULE_NAME =
"dxgi.dll";
88 static std::FILE *s_pFileStdout = stdout;
89 static std::FILE *s_pFileStderr = stderr;
91 static IDXGISwapChainPresentPtr s_pSwapChainPresentTrampoline =
nullptr;
92 static HWND s_hWnd =
nullptr;
94 static WNDPROC s_pWndProcApp =
nullptr;
95 static bool s_bInitialized =
false, s_bWidgetsEnabled =
false;
98 static ImGuiContext *s_pImGuiContext =
nullptr;
99 #if defined FMGUI_ENABLE_IMPLOT
100 static ImPlotContext *s_pImPlotContext =
nullptr;
102 static bool s_bImplWin32Initialized =
false;
103 static bool s_bImplDX11Initialized =
false;
106 static std::stack<Message> s_msgStack;
107 static constexpr std::stack<Message>::size_type kMSG_STACK_MAX_SIZE = 24;
111 #define PUSH_MSG(SEVERITY, CONTENT) ;
129 s_pWidgetRoutine = pRoutine;
134 s_pInputRoutine = pInputRoutine;
139 s_pMessageCallback = pMessageCallback;
144 std::ostringstream oss;
147 <<
"ID3D11Device Pointer Location: "
148 <<
reinterpret_cast<void *
>(&s_pDevice) <<
'\n'
149 <<
"ID3D11DeviceContext Pointer Location: "
150 <<
reinterpret_cast<void *
>(&s_pDeviceContext) <<
'\n'
151 <<
"ID3D11RenderTargetView Pointer Location: "
152 <<
reinterpret_cast<void *
>(&s_pRenderTargetView) <<
'\n';
160 ID3D11InfoQueue *pInfoQueue =
nullptr;
161 if (!s_pDevice || FAILED(s_pDevice->QueryInterface(__uuidof(ID3D11InfoQueue),
162 reinterpret_cast<void **
>(&pInfoQueue))))
164 PUSH_MSG(MessageSeverity::kHIGH,
"QueryInterface failed!");
165 return std::string();
168 if (FAILED(pInfoQueue->PushEmptyStorageFilter()))
170 PUSH_MSG(MessageSeverity::kHIGH,
"ID3D11InfoQueue::PushEmptyStorageFilter failed!");
171 return std::string();
174 std::ostringstream oss;
175 const UINT64 kMsgCount = pInfoQueue->GetNumStoredMessages();
177 for (UINT64 index = 0; index < kMsgCount; ++index)
181 if (FAILED(pInfoQueue->GetMessage(index,
nullptr, &msgSize)))
183 PUSH_MSG(MessageSeverity::kHIGH,
"GetMessage failed!");
184 return std::string();
187 D3D11_MESSAGE *pMessage = (D3D11_MESSAGE *)std::malloc(msgSize);
190 PUSH_MSG(MessageSeverity::kHIGH,
"std::malloc failed!");
191 return std::string();
194 if (FAILED(pInfoQueue->GetMessage(index, pMessage, &msgSize)))
196 PUSH_MSG(MessageSeverity::kHIGH,
"ID3D11InfoQueue::GetMessaged failed!");
198 return std::string();
201 oss <<
"D3D11 MESSAGE|ID:" <<
static_cast<int>(pMessage->ID)
202 <<
"|CATEGORY:" <<
static_cast<int>(pMessage->Category)
203 <<
"|SEVERITY:" <<
static_cast<int>(pMessage->Severity)
204 <<
"|DESC_LEN:" << pMessage->DescriptionByteLength
205 <<
"|DESC:" << pMessage->pDescription;
208 pInfoQueue->ClearStoredMessages();
209 ReleaseCOM(pInfoQueue);
210 return std::string();
213 LPVOID FmGui::LookupSwapChainVTable(
void)
220 WNDCLASSEXA wndClassEx;
221 ZeroMemory(&wndClassEx,
sizeof(wndClassEx));
222 wndClassEx.cbSize =
sizeof(WNDCLASSEXA);
223 wndClassEx.style = CS_HREDRAW | CS_VREDRAW;
224 wndClassEx.lpfnWndProc =
nullptr;
225 wndClassEx.cbClsExtra = 0;
226 wndClassEx.cbWndExtra = 0;
227 wndClassEx.hInstance = GetModuleHandle(
nullptr);
228 wndClassEx.hIcon =
nullptr;
229 wndClassEx.hCursor =
nullptr;
230 wndClassEx.hbrBackground =
nullptr;
231 wndClassEx.lpszMenuName =
nullptr;
232 wndClassEx.lpszClassName =
"FmGuiWndClassName";
233 wndClassEx.hIconSm =
nullptr;
235 if (RegisterClassExA(&wndClassEx) == 0)
237 PUSH_MSG(MessageSeverity::kHIGH,
"RegisterClassEx failed!");
241 constexpr
int kFAKE_WND_WIDTH = 100, kFAKE_WND_HEIGHT = 100;
242 HWND hLocalWnd = CreateWindowExA(
244 wndClassEx.lpszClassName,
247 CW_USEDEFAULT, CW_USEDEFAULT,
248 kFAKE_WND_WIDTH, kFAKE_WND_HEIGHT,
251 wndClassEx.hInstance,
257 PUSH_MSG(MessageSeverity::kHIGH,
"CreateWindowEx failed!");
258 UnregisterClassA(wndClassEx.lpszClassName, wndClassEx.hInstance);
266 D3D_FEATURE_LEVEL featureLevel;
267 const D3D_FEATURE_LEVEL kFeatureLevels[] = {
268 D3D_FEATURE_LEVEL_10_1,
269 D3D_FEATURE_LEVEL_11_0
271 constexpr UINT kNUM_FEAT_LEVELS = std::size(kFeatureLevels);
273 UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
275 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
277 ID3D11Device *pLocalDevice =
nullptr;
278 ID3D11DeviceContext *pLocalDeviceContext =
nullptr;
279 IDXGISwapChain *pLocalSwapChain =
nullptr;
281 DXGI_RATIONAL refreshRateRational;
282 ZeroMemory(&refreshRateRational,
sizeof(refreshRateRational));
283 refreshRateRational.Numerator = 60;
284 refreshRateRational.Denominator = 1;
286 DXGI_MODE_DESC bufferModeDesc;
287 ZeroMemory(&bufferModeDesc,
sizeof(bufferModeDesc));
288 bufferModeDesc.Width = 100;
289 bufferModeDesc.Height = 100;
290 bufferModeDesc.RefreshRate = refreshRateRational;
291 bufferModeDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
292 bufferModeDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
294 DXGI_SAMPLE_DESC sampleDesc;
295 ZeroMemory(&sampleDesc,
sizeof(sampleDesc));
296 sampleDesc.Count = 1;
297 sampleDesc.Quality = 0;
299 DXGI_SWAP_CHAIN_DESC swapChainDesc;
300 ZeroMemory(&swapChainDesc,
sizeof(swapChainDesc));
301 swapChainDesc.BufferDesc = bufferModeDesc;
302 swapChainDesc.SampleDesc = sampleDesc;
303 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
304 swapChainDesc.BufferCount = 1;
306 HWND hWndFg = GetForegroundWindow();
309 PUSH_MSG(MessageSeverity::kHIGH,
"GetForegroundWindow failed!");
314 constexpr std::size_t kTITLE_BUFF_SIZE = 256;
315 CHAR title[kTITLE_BUFF_SIZE];
316 GetWindowTextA(hWndFg, title, kTITLE_BUFF_SIZE);
318 char buffer[kTITLE_BUFF_SIZE];
319 std::snprintf(buffer, kTITLE_BUFF_SIZE,
"FmGui has window \"%s\".", title);
320 PUSH_MSG(MessageSeverity::kNOTIFICATION, std::string(buffer, kTITLE_BUFF_SIZE));
323 swapChainDesc.OutputWindow = hWndFg;
324 swapChainDesc.Windowed = TRUE;
325 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
326 swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
327 if (FAILED(D3D11CreateDeviceAndSwapChain(
329 D3D_DRIVER_TYPE_HARDWARE,
339 &pLocalDeviceContext)))
341 PUSH_MSG(MessageSeverity::kHIGH,
"D3D11CreateDeviceAndSwapChain failed!");
347 DWORD_PTR *pLocalSwapChainVTable =
nullptr;
348 pLocalSwapChainVTable =
reinterpret_cast<DWORD_PTR *
>(pLocalSwapChain);
349 pLocalSwapChainVTable =
reinterpret_cast<DWORD_PTR *
>(pLocalSwapChainVTable[0]);
350 LPVOID result =
reinterpret_cast<LPVOID
>(pLocalSwapChainVTable[8]);
352 ReleaseCOM(pLocalDevice);
353 ReleaseCOM(pLocalDeviceContext);
354 ReleaseCOM(pLocalSwapChain);
362 bool bPrevValue = s_bWidgetsEnabled;
363 s_bWidgetsEnabled = bEnabled;
367 std::string FmGui::MinHookStatusToStdString(MH_STATUS mhStatus)
369 constexpr std::size_t kBUFF_SIZE = 124;
370 char buffer[kBUFF_SIZE];
372 const int kWritten = std::snprintf(buffer, kBUFF_SIZE,
"%s", MH_StatusToString(mhStatus));
373 return (kWritten > 0) ? std::string(buffer, kWritten) :
"";
381 PUSH_MSG(MessageSeverity::kNOTIFICATION,
"Redirecting Direct3D routines...");
384 LPVOID pSwapChainPresentOriginal = LookupSwapChainVTable();
385 if (!pSwapChainPresentOriginal)
387 PUSH_MSG(MessageSeverity::kHIGH,
"FmGui::LookupSwapChainVTable!");
395 MH_STATUS mhStatus = MH_Initialize();
396 if (mhStatus != MH_OK)
398 PUSH_MSG(MessageSeverity::kHIGH,
"MH_Initialize failed: " + MinHookStatusToStdString(mhStatus) +
'!');
403 mhStatus = MH_CreateHook(pSwapChainPresentOriginal, &SwapChainPresentImpl,
404 reinterpret_cast<LPVOID *
>(&s_pSwapChainPresentTrampoline));
405 if (mhStatus != MH_OK)
407 PUSH_MSG(MessageSeverity::kHIGH,
"MH_CreateHook failed: " + MinHookStatusToStdString(mhStatus) +
'!');
412 mhStatus = MH_EnableHook(pSwapChainPresentOriginal);
413 if (mhStatus != MH_OK)
415 PUSH_MSG(MessageSeverity::kHIGH,
"MH_EnableHook failed: " + MinHookStatusToStdString(mhStatus) +
'!');
419 PUSH_MSG(MessageSeverity::kNOTIFICATION,
"Direct3D Redirection complete.");
423 HRESULT FMGUI_FASTCALL FmGui::SwapChainPresentImpl(IDXGISwapChain *pSwapChain, UINT syncInterval, UINT flags)
431 PUSH_MSG(MessageSeverity::kNOTIFICATION,
"Setting up present hook...");
433 hResult = GetDevice(pSwapChain, &s_pDevice);
436 PUSH_MSG(MessageSeverity::kHIGH,
"FmGui::GetDevice failed!");
437 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
440 hResult = GetDeviceContext(pSwapChain, &s_pDevice, &s_pDeviceContext);
443 PUSH_MSG(MessageSeverity::kHIGH,
"FmGui::GetDeviceContext failed!");
444 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
447 s_pImGuiContext = ImGui::CreateContext();
448 if (!s_pImGuiContext)
450 PUSH_MSG(MessageSeverity::kHIGH,
"ImGui::CreateContext failed!");
451 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
453 #ifdef FMGUI_ENABLE_IMPLOT
454 s_pImPlotContext = ImPlot::CreateContext();
455 if (!s_pImPlotContext)
457 PUSH_MSG(MessageSeverity::kHIGH,
"ImPlot::CreateContext failed!");
458 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
461 ImGui::SetCurrentContext(s_pImGuiContext);
462 #ifdef FMGUI_ENABLE_IMPLOT
463 ImPlot::SetCurrentContext(s_pImPlotContext);
465 ImGuiIO &imGuiIO = ImGui::GetIO();
468 #ifdef FMGUI_ENABLE_IMPLOT
476 imGuiIO.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
479 imGuiIO.IniFilename =
nullptr;
481 imGuiIO.IniFilename = s_config.
iniFileName.c_str();
484 switch (s_config.
style)
486 case Style::kCLASSIC:
487 ImGui::StyleColorsClassic();
490 ImGui::StyleColorsDark();
493 ImGui::StyleColorsLight();
498 DXGI_SWAP_CHAIN_DESC swapChainDesc;
499 ZeroMemory(&swapChainDesc,
sizeof(swapChainDesc));
500 if (FAILED(pSwapChain->GetDesc(&swapChainDesc)))
502 PUSH_MSG(MessageSeverity::kHIGH,
"IDXGISwapChain::GetDesc failed!");
503 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
506 s_hWnd = swapChainDesc.OutputWindow;
507 s_pWndProcApp =
reinterpret_cast<WNDPROC
>(SetWindowLongPtr(s_hWnd, GWLP_WNDPROC,
508 reinterpret_cast<LONG_PTR
>(WndProc)));
509 if (s_pWndProcApp == NULL)
511 PUSH_MSG(MessageSeverity::kHIGH,
"SetWindowLongPtr failed!");
516 bResult = ImGui_ImplWin32_Init(s_hWnd);
517 s_bImplWin32Initialized = bResult;
520 PUSH_MSG(MessageSeverity::kHIGH,
"ImGui_ImplWin32_Init failed!");
524 bResult = ImGui_ImplDX11_Init(s_pDevice, s_pDeviceContext);
525 s_bImplDX11Initialized = bResult;
528 PUSH_MSG(MessageSeverity::kHIGH,
"ImGui_ImplDX11_Init failed!");
531 imGuiIO.ImeWindowHandle = s_hWnd;
536 ID3D11Texture2D *pSwapChainBackBuffer =
nullptr;
537 hResult = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
reinterpret_cast<LPVOID *
>(&pSwapChainBackBuffer));
540 PUSH_MSG(MessageSeverity::kHIGH,
"IDXGISwapChain::GetBuffer failed!");
544 hResult = s_pDevice->CreateRenderTargetView(pSwapChainBackBuffer,
nullptr, &s_pRenderTargetView);
547 PUSH_MSG(MessageSeverity::kHIGH,
"ID3D11Device::CreateRenderTargetView failed!");
550 ReleaseCOM(pSwapChainBackBuffer);
552 s_bInitialized =
true;
557 ImGui::SetCurrentContext(s_pImGuiContext);
558 #ifdef FMGUI_ENABLE_IMPLOT
559 ImPlot::SetCurrentContext(s_pImPlotContext);
562 if (!ImGui::GetCurrentContext())
563 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
565 ImGui_ImplWin32_NewFrame();
566 ImGui_ImplDX11_NewFrame();
569 if (s_bWidgetsEnabled)
571 if (s_pWidgetRoutine !=
nullptr)
577 s_pDeviceContext->OMSetRenderTargets(1, &s_pRenderTargetView,
nullptr);
578 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
580 return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
583 HRESULT FmGui::GetDevice(IDXGISwapChain *
const pSwapChain, ID3D11Device **ppDevice)
585 HRESULT retVal = pSwapChain->GetDevice(__uuidof(ID3D11Device),
reinterpret_cast<PVOID *
>(ppDevice));
589 HRESULT FmGui::GetDeviceContext(IDXGISwapChain *
const pSwapChain, ID3D11Device **ppDevice, ID3D11DeviceContext **ppDeviceContext)
591 (*ppDevice)->GetImmediateContext(ppDeviceContext);
592 return (ppDeviceContext !=
nullptr) ? S_OK : E_FAIL;
598 MH_STATUS mhStatus = MH_DisableHook(MH_ALL_HOOKS);
599 if (mhStatus != MH_OK)
601 PUSH_MSG(MessageSeverity::kHIGH,
"MH_DisableHook failed: " + MinHookStatusToStdString(mhStatus) +
'!');
605 mhStatus = MH_Uninitialize();
606 if (mhStatus != MH_OK)
608 PUSH_MSG(MessageSeverity::kHIGH,
"MH_Uninitialize failed: " + MinHookStatusToStdString(mhStatus) +
'!');
612 if (s_bImplDX11Initialized)
614 ImGui_ImplDX11_Shutdown();
615 s_bImplDX11Initialized =
false;
618 if (s_bImplWin32Initialized)
620 ImGui_ImplWin32_Shutdown();
621 s_bImplWin32Initialized =
false;
623 #if defined FMGUI_ENABLE_IMPLOT
624 if (s_pImPlotContext !=
nullptr)
626 ImPlot::DestroyContext(s_pImPlotContext);
630 if (s_pImGuiContext !=
nullptr)
632 ImGui::DestroyContext(s_pImGuiContext);
633 s_pImGuiContext =
nullptr;
636 ReleaseCOM(s_pDevice);
637 ReleaseCOM(s_pDeviceContext);
638 ReleaseCOM(s_pRenderTargetView);
642 if (SetWindowLongPtr(s_hWnd, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR
>(s_pWndProcApp)) == 0)
644 PUSH_MSG(MessageSeverity::kHIGH,
"SetWindowLongPtr failed!");
649 s_bInitialized =
false;
655 return s_msgStack.top();
660 MessageVector errorMsgs;
661 while (!s_msgStack.empty())
663 errorMsgs.push_back(s_msgStack.top());
666 std::reverse(std::begin(errorMsgs), std::end(errorMsgs));
670 void FmGui::OnResize(IDXGISwapChain *pSwapChain, UINT newWidth, UINT newHeight)
682 ReleaseCOM(s_pRenderTargetView);
684 DXGI_SWAP_CHAIN_DESC swapChainDesc;
685 ZeroMemory(&swapChainDesc,
sizeof(swapChainDesc));
686 if (FAILED(pSwapChain->GetDesc(&swapChainDesc)))
688 PUSH_MSG(MessageSeverity::kHIGH,
"IDXGISwapChain::GetDesc failed!");
692 if (FAILED(pSwapChain->ResizeBuffers(swapChainDesc.BufferCount,
694 swapChainDesc.BufferDesc.Format, 0u)))
696 PUSH_MSG(MessageSeverity::kHIGH,
"IDXGISwapChain::ResizeBuffers failed!");
700 ID3D11Texture2D *pSwapChainBackBuffer =
nullptr;
701 if (FAILED(pSwapChain->GetBuffer(0u, __uuidof(ID3D11Texture2D),
702 reinterpret_cast<void **
>(&pSwapChainBackBuffer))))
704 PUSH_MSG(MessageSeverity::kHIGH,
"IDXGISwapChain::GetBuffer failed!");
705 ReleaseCOM(pSwapChainBackBuffer);
709 if (FAILED(s_pDevice->CreateRenderTargetView(pSwapChainBackBuffer,
nullptr, &s_pRenderTargetView)))
711 PUSH_MSG(MessageSeverity::kHIGH,
"ID3D11Device::CreateRenderTargetView failed!");
712 ReleaseCOM(pSwapChainBackBuffer);
715 ReleaseCOM(pSwapChainBackBuffer);
717 s_pDeviceContext->OMSetRenderTargets(1, &s_pRenderTargetView,
nullptr);
718 CD3D11_VIEWPORT viewport(0.0f, 0.0f,
static_cast<float>(newWidth),
static_cast<float>(newHeight), 0.0f, 1.0f);
719 s_pDeviceContext->RSSetViewports(1, &viewport);
722 LRESULT FmGui::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
725 if (ImGui::GetCurrentContext())
727 ImGuiIO &imGuiIO = ImGui::GetIO();
728 POINT cursorPos; GetCursorPos(&cursorPos);
731 ScreenToClient(s_hWnd, &cursorPos);
733 imGuiIO.MousePos.x = cursorPos.x;
734 imGuiIO.MousePos.y = cursorPos.y;
737 if (s_bWidgetsEnabled)
740 if (ImGui::GetCurrentContext() && ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam))
745 if (s_pInputRoutine !=
nullptr)
746 s_pInputRoutine(uMsg, wParam, lParam);
753 const UINT newWidth =
static_cast<UINT
>(LOWORD(lParam));
754 const UINT newHeight =
static_cast<UINT
>(HIWORD(lParam));
762 return CallWindowProc(s_pWndProcApp, hWnd, uMsg, wParam, lParam);
void SetMessageCallback(MessageCallback pMessageCallback)
bool StartupHook(const Config &config=Config())
std::string AddressDump(void)
std::vector< Message > GetEveryMessage(void)
void SetRoutinePtr(RoutinePtr pRoutine)
std::add_pointer< void(void)>::type RoutinePtr
Alias for FmGui routine function.
void SetInputRoutinePtr(InputRoutinePtr pInputRoutine)
std::add_pointer< void(UINT uMsg, WPARAM wParam, LPARAM lParam)>::type InputRoutinePtr
Alias for FmGui input routine function.
std::add_pointer< void(const Message &message)>::type MessageCallback
Alias for message callback.
bool SetWidgetVisibility(bool bEnabled)
const Message & GetLastError(void)
std::string DebugLayerMessageDump(void)
FmGui configuration data.
ImGuiConfigFlags configFlags