From 81c2748027c926afbe98a63f0833a43675e9f850 Mon Sep 17 00:00:00 2001 From: narzoul Date: Sun, 1 Sep 2019 15:45:36 +0200 Subject: [PATCH] Improved hooking of user32 window procedures --- DDrawCompat/Gdi/PaintHandlers.cpp | 219 ++++++++++++++++++------------ 1 file changed, 134 insertions(+), 85 deletions(-) diff --git a/DDrawCompat/Gdi/PaintHandlers.cpp b/DDrawCompat/Gdi/PaintHandlers.cpp index 447fe87..cd69030 100644 --- a/DDrawCompat/Gdi/PaintHandlers.cpp +++ b/DDrawCompat/Gdi/PaintHandlers.cpp @@ -21,17 +21,12 @@ namespace struct User32WndProc { - WNDPROC oldWndProcTrampoline; WNDPROC oldWndProc; + WNDPROC oldWndProcTrampoline; WNDPROC newWndProc; - std::string name; - - User32WndProc() - : oldWndProcTrampoline(nullptr) - , oldWndProc(nullptr) - , newWndProc(nullptr) - { - } + std::string procName; + std::string className; + bool isUnicode; }; LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc); @@ -42,12 +37,8 @@ namespace LRESULT onPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); - int g_menuWndProcIndex = 0; - int g_scrollBarWndProcIndex = 0; - - std::vector g_user32WndProcA; - std::vector g_user32WndProcW; - std::vector g_user32WndProcHook; + User32WndProc* g_currentUser32WndProc = nullptr; + std::vector g_failedHooks; LRESULT buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) { @@ -71,6 +62,38 @@ namespace } } + LRESULT CALLBACK cbtProc(int nCode, WPARAM wParam, LPARAM lParam) + { + if (HCBT_CREATEWND == nCode && g_currentUser32WndProc) + { + const auto hwnd = reinterpret_cast(wParam); + const bool isUnicode = IsWindowUnicode(hwnd); + char className[32] = {}; + GetClassName(hwnd, className, sizeof(className)); + + if (0 == _stricmp(className, g_currentUser32WndProc->className.c_str()) && + isUnicode == g_currentUser32WndProc->isUnicode && + !g_currentUser32WndProc->oldWndProc) + { + decltype(&GetWindowLong) getWindowLong = isUnicode ? GetWindowLongW : GetWindowLongA; + auto wndProc = reinterpret_cast(getWindowLong(hwnd, GWL_WNDPROC)); + HMODULE wndProcModule = nullptr; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(wndProc), &wndProcModule); + + if (wndProcModule && wndProcModule == GetModuleHandle("user32")) + { + g_currentUser32WndProc->oldWndProc = wndProc; + g_currentUser32WndProc->oldWndProcTrampoline = wndProc; + Compat::hookFunction(reinterpret_cast(g_currentUser32WndProc->oldWndProcTrampoline), + g_currentUser32WndProc->newWndProc); + } + } + } + + return CallNextHookEx(nullptr, nCode, wParam, lParam); + } + LRESULT comboBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) { return defPaintProc(hwnd, msg, wParam, lParam, origWndProc); @@ -163,39 +186,66 @@ namespace return result; } - void hookUser32WndProc(const std::string& wndProcName, HWND hwnd, WNDPROC newWndProc, - decltype(GetWindowLongPtr)* getWindowLong, std::vector& user32WndProc) + template + User32WndProc& getUser32WndProcA() { - User32WndProc wndProc; - wndProc.oldWndProc = - reinterpret_cast(getWindowLong(hwnd, GWL_WNDPROC)); - wndProc.oldWndProcTrampoline = wndProc.oldWndProc; - wndProc.newWndProc = newWndProc; - wndProc.name = wndProcName; - user32WndProc.push_back(wndProc); + static User32WndProc user32WndProcA = {}; + return user32WndProcA; + } - if (reinterpret_cast(wndProc.oldWndProcTrampoline) < 0xFFFF0000) + template + User32WndProc& getUser32WndProcW() + { + static User32WndProc user32WndProcW = {}; + return user32WndProcW; + } + + void hookUser32WndProc(User32WndProc& user32WndProc, WNDPROC newWndProc, + const std::string& procName, const std::string& className, bool isUnicode) + { + CLIENTCREATESTRUCT ccs = {}; + user32WndProc.newWndProc = newWndProc; + user32WndProc.procName = procName; + user32WndProc.className = className; + user32WndProc.isUnicode = isUnicode; + + g_currentUser32WndProc = &user32WndProc; + if (isUnicode) { - Compat::hookFunction( - reinterpret_cast(user32WndProc.back().oldWndProcTrampoline), newWndProc); + DestroyWindow(CreateWindowW(std::wstring(className.begin(), className.end()).c_str(), L"", 0, 0, 0, 0, 0, + nullptr, nullptr, nullptr, &ccs)); + } + else + { + DestroyWindow(CreateWindowA(className.c_str(), "", 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, &ccs)); + } + g_currentUser32WndProc = nullptr; + + if (user32WndProc.oldWndProc == user32WndProc.oldWndProcTrampoline) + { + g_failedHooks.push_back(user32WndProc.procName); } } - void hookUser32WndProcA(const char* className, WNDPROC newWndProc, const std::string& wndProcName) + template + void hookUser32WndProcA(const std::string& className, const std::string& procName) { - CLIENTCREATESTRUCT ccs = {}; - HWND hwnd = CreateWindowA(className, "", 0, 0, 0, 0, 0, 0, 0, 0, &ccs); - hookUser32WndProc(wndProcName + 'A', hwnd, newWndProc, CALL_ORIG_FUNC(GetWindowLongA), g_user32WndProcA); - DestroyWindow(hwnd); + hookUser32WndProc(getUser32WndProcA(), user32WndProcA, + procName + 'A', className, false); } - void hookUser32WndProcW(const char* name, WNDPROC newWndProc, const std::string& wndProcName) + template + void hookUser32WndProcW(const std::string& className, const std::string& procName) { - CLIENTCREATESTRUCT ccs = {}; - HWND hwnd = CreateWindowW( - std::wstring(name, name + std::strlen(name)).c_str(), L"", 0, 0, 0, 0, 0, 0, 0, 0, &ccs); - hookUser32WndProc(wndProcName + 'W', hwnd, newWndProc, CALL_ORIG_FUNC(GetWindowLongW), g_user32WndProcW); - DestroyWindow(hwnd); + hookUser32WndProc(getUser32WndProcW(), user32WndProcW, + procName + 'W', className, true); + } + + template + void hookUser32WndProc(const std::string& className, const std::string& procName) + { + hookUser32WndProcA(className, procName); + hookUser32WndProcW(className, procName); } LRESULT listBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) @@ -369,22 +419,24 @@ namespace } LRESULT CALLBACK user32WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, - const User32WndProc& user32WndProc, WndProcHook wndProcHook) + const std::string& procName, WndProcHook wndProcHook, WNDPROC oldWndProcTrampoline) { - LOG_FUNC(user32WndProc.name.c_str(), hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam)); - return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, user32WndProc.oldWndProcTrampoline)); + LOG_FUNC(procName.c_str(), hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam)); + return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline)); } - template + template LRESULT CALLBACK user32WndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - return user32WndProc(hwnd, uMsg, wParam, lParam, g_user32WndProcA[index], g_user32WndProcHook[index]); + auto& wp = getUser32WndProcA(); + return user32WndProc(hwnd, uMsg, wParam, lParam, wp.procName, wndProcHook, wp.oldWndProcTrampoline); } - template + template LRESULT CALLBACK user32WndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - return user32WndProc(hwnd, uMsg, wParam, lParam, g_user32WndProcW[index], g_user32WndProcHook[index]); + auto& wp = getUser32WndProcW(); + return user32WndProc(hwnd, uMsg, wParam, lParam, wp.procName, wndProcHook, wp.oldWndProcTrampoline); } } @@ -396,28 +448,33 @@ namespace Gdi { disableImmersiveContextMenus(); -#define HOOK_USER32_WNDPROC(index, name, wndProcHook) \ - g_user32WndProcHook.push_back(wndProcHook); \ - hookUser32WndProcA(name, user32WndProcA, #wndProcHook); \ - hookUser32WndProcW(name, user32WndProcW, #wndProcHook) +#define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc(className, #wndProcHook) +#define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW(className, #wndProcHook) - g_user32WndProcA.reserve(9); - g_user32WndProcW.reserve(9); + auto hook = SetWindowsHookEx(WH_CBT, cbtProc, nullptr, GetCurrentThreadId()); - HOOK_USER32_WNDPROC(0, "Button", buttonWndProc); - HOOK_USER32_WNDPROC(1, "ComboBox", comboBoxWndProc); - HOOK_USER32_WNDPROC(2, "Edit", editWndProc); - HOOK_USER32_WNDPROC(3, "ListBox", listBoxWndProc); - HOOK_USER32_WNDPROC(4, "MDIClient", mdiClientWndProc); - HOOK_USER32_WNDPROC(5, "ScrollBar", scrollBarWndProc); - HOOK_USER32_WNDPROC(6, "Static", staticWndProc); - HOOK_USER32_WNDPROC(7, "ComboLBox", comboListBoxWndProc); - HOOK_USER32_WNDPROC(8, "#32768", menuWndProc); + HOOK_USER32_WNDPROC("Button", buttonWndProc); + HOOK_USER32_WNDPROC("ComboBox", comboBoxWndProc); + HOOK_USER32_WNDPROC("Edit", editWndProc); + HOOK_USER32_WNDPROC("ListBox", listBoxWndProc); + HOOK_USER32_WNDPROC("MDIClient", mdiClientWndProc); + HOOK_USER32_WNDPROC("Static", staticWndProc); + HOOK_USER32_WNDPROC("ComboLBox", comboListBoxWndProc); - g_scrollBarWndProcIndex = 5; - g_menuWndProcIndex = 8; + HOOK_USER32_WNDPROCW("ScrollBar", scrollBarWndProc); + HOOK_USER32_WNDPROCW("#32768", menuWndProc); + + UnhookWindowsHookEx(hook); #undef HOOK_USER32_WNDPROC +#undef HOOK_USER32_WNDPROCW + + if (!g_failedHooks.empty()) + { + Compat::Log() << "Warning: Failed to hook the following user32 window procedures: " << + Compat::array(g_failedHooks.data(), g_failedHooks.size()); + g_failedHooks.clear(); + } HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA); HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW); @@ -427,33 +484,25 @@ namespace Gdi void onCreateWindow(HWND hwnd) { - WNDPROC wndProcA = reinterpret_cast(GetWindowLongA(hwnd, GWL_WNDPROC)); - WNDPROC wndProcW = reinterpret_cast(GetWindowLongW(hwnd, GWL_WNDPROC)); - - int index = -1; - if (wndProcA == g_user32WndProcA[g_menuWndProcIndex].oldWndProc || - wndProcW == g_user32WndProcW[g_menuWndProcIndex].oldWndProc) + if (!IsWindowUnicode(hwnd)) { - index = g_menuWndProcIndex; - } - else if (wndProcA == g_user32WndProcA[g_scrollBarWndProcIndex].oldWndProc || - wndProcW == g_user32WndProcW[g_scrollBarWndProcIndex].oldWndProc) - { - index = g_scrollBarWndProcIndex; + return; } - if (-1 != index) + const auto wndProc = reinterpret_cast(GetWindowLongW(hwnd, GWL_WNDPROC)); + User32WndProc* user32WndProc = nullptr; + if (getUser32WndProcW().oldWndProc == wndProc) { - if (IsWindowUnicode(hwnd)) - { - CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC, - reinterpret_cast(g_user32WndProcW[index].newWndProc)); - } - else - { - CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, - reinterpret_cast(g_user32WndProcA[index].newWndProc)); - } + user32WndProc = &getUser32WndProcW(); + } + else if (getUser32WndProcW().oldWndProc == wndProc) + { + user32WndProc = &getUser32WndProcW(); + } + + if (user32WndProc) + { + SetWindowLongW(hwnd, GWL_WNDPROC, reinterpret_cast(user32WndProc->newWndProc)); } } }