From f3718272cd5b4f114441bf6b1f4cacde8df3e84f Mon Sep 17 00:00:00 2001 From: narzoul Date: Mon, 4 Jan 2021 21:21:05 +0100 Subject: [PATCH] Hook ASCII window procedures for menus and scroll bars Fixes missing texts in menus of PGA Championship Golf 2000 (issue #84). --- DDrawCompat/Common/Hook.cpp | 64 ++++++++++------- DDrawCompat/Common/Hook.h | 2 + DDrawCompat/Gdi/User32WndProcs.cpp | 109 +++++++++-------------------- 3 files changed, 72 insertions(+), 103 deletions(-) diff --git a/DDrawCompat/Common/Hook.cpp b/DDrawCompat/Common/Hook.cpp index 95c0ddf..ded6b1d 100644 --- a/DDrawCompat/Common/Hook.cpp +++ b/DDrawCompat/Common/Hook.cpp @@ -24,7 +24,6 @@ namespace std::map g_hookedFunctions; PIMAGE_NT_HEADERS getImageNtHeaders(HMODULE module); - HMODULE getModuleHandleFromAddress(void* address); std::filesystem::path getModulePath(HMODULE module); std::map::iterator findOrigFunc(void* origFunc) @@ -84,7 +83,7 @@ namespace std::string funcAddrToStr(void* funcPtr) { std::ostringstream oss; - HMODULE module = getModuleHandleFromAddress(funcPtr); + HMODULE module = Compat::getModuleHandleFromAddress(funcPtr); oss << getModulePath(module).string() << "+0x" << std::hex << reinterpret_cast(funcPtr) - reinterpret_cast(module); return oss.str(); @@ -108,14 +107,6 @@ namespace return ntHeaders; } - HMODULE getModuleHandleFromAddress(void* address) - { - HMODULE module = nullptr; - GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - static_cast(address), &module); - return module; - } - std::filesystem::path getModulePath(HMODULE module) { char path[MAX_PATH] = {}; @@ -125,6 +116,22 @@ namespace void hookFunction(void*& origFuncPtr, void* newFuncPtr, const char* funcName) { + void* stubFuncPtr = nullptr; + if (GetModuleHandle("ntdll") == Compat::getModuleHandleFromAddress(origFuncPtr)) + { + // Avoid hooking ntdll stubs (e.g. ntdll/NtdllDialogWndProc_A instead of user32/DefDlgProcA) + if (0xFF == reinterpret_cast(origFuncPtr)[0] && + 0x25 == reinterpret_cast(origFuncPtr)[1]) + { + void* jmpTarget = **reinterpret_cast(reinterpret_cast(origFuncPtr) + 2); + if (GetModuleHandle("user32") == Compat::getModuleHandleFromAddress(jmpTarget)) + { + stubFuncPtr = origFuncPtr; + origFuncPtr = jmpTarget; + } + } + } + const auto it = findOrigFunc(origFuncPtr); if (it != g_hookedFunctions.end()) { @@ -140,11 +147,15 @@ namespace } void* const hookedFuncPtr = origFuncPtr; - HMODULE module = nullptr; - GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - static_cast(hookedFuncPtr), &module); - - LOG_DEBUG << "Hooking function: " << funcName << " (" << funcAddrToStr(hookedFuncPtr) << ')'; + if (stubFuncPtr) + { + LOG_DEBUG << "Hooking function: " << funcName << " (" << funcAddrToStr(stubFuncPtr) << " -> " + << funcAddrToStr(origFuncPtr) << ')'; + } + else + { + LOG_DEBUG << "Hooking function: " << funcName << " (" << funcAddrToStr(hookedFuncPtr) << ')'; + } DetourTransactionBegin(); const bool attachSuccessful = NO_ERROR == DetourAttach(&origFuncPtr, newFuncPtr); @@ -155,6 +166,9 @@ namespace return; } + HMODULE module = nullptr; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast(hookedFuncPtr), &module); + g_hookedFunctions.emplace( std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr })); } @@ -175,6 +189,14 @@ namespace namespace Compat { + HMODULE getModuleHandleFromAddress(void* address) + { + HMODULE module = nullptr; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + static_cast(address), &module); + return module; + } + FARPROC getProcAddress(HMODULE module, const char* procName) { if (!module || !procName) @@ -245,18 +267,6 @@ namespace Compat } } - // Avoid hooking ntdll stubs (e.g. ntdll/NtdllDialogWndProc_A instead of user32/DefDlgProcA) - if (func && getModuleHandleFromAddress(func) != module && - 0xFF == static_cast(func[0]) && - 0x25 == static_cast(func[1])) - { - FARPROC jmpTarget = **reinterpret_cast(func + 2); - if (getModuleHandleFromAddress(jmpTarget) == module) - { - return jmpTarget; - } - } - return reinterpret_cast(func); } diff --git a/DDrawCompat/Common/Hook.h b/DDrawCompat/Common/Hook.h index 895d906..06b8839 100644 --- a/DDrawCompat/Common/Hook.h +++ b/DDrawCompat/Common/Hook.h @@ -13,6 +13,8 @@ namespace Compat { + HMODULE getModuleHandleFromAddress(void* address); + template OrigFuncPtr& getOrigFuncPtr() { diff --git a/DDrawCompat/Gdi/User32WndProcs.cpp b/DDrawCompat/Gdi/User32WndProcs.cpp index 596f69c..e48fc39 100644 --- a/DDrawCompat/Gdi/User32WndProcs.cpp +++ b/DDrawCompat/Gdi/User32WndProcs.cpp @@ -50,9 +50,6 @@ namespace LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); LRESULT onSetText(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc); - User32WndProc* g_currentUser32WndProc = nullptr; - std::vector g_failedHooks; - LRESULT buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) { switch (msg) @@ -75,39 +72,6 @@ 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 ? CALL_ORIG_FUNC(GetWindowLongW) : CALL_ORIG_FUNC(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, - g_currentUser32WndProc->procName.c_str()); - } - } - } - - 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); @@ -261,28 +225,36 @@ namespace void hookUser32WndProc(User32WndProc& user32WndProc, WNDPROC newWndProc, const std::string& procName, const std::string& className, bool isUnicode) { - CLIENTCREATESTRUCT ccs = {}; + WNDPROC wndProc = nullptr; + if (isUnicode) + { + WNDCLASSW wc = {}; + GetClassInfoW(nullptr, std::wstring(className.begin(), className.end()).c_str(), &wc); + wndProc = wc.lpfnWndProc; + } + else + { + WNDCLASSA wc = {}; + GetClassInfoA(nullptr, className.c_str(), &wc); + wndProc = wc.lpfnWndProc; + } + + HMODULE module = Compat::getModuleHandleFromAddress(wndProc); + if (module != GetModuleHandle("ntdll") && module != GetModuleHandle("user32")) + { + Compat::Log() << "Failed to hook a user32 window procedure: " << className; + return; + } + + user32WndProc.oldWndProc = wndProc; + user32WndProc.oldWndProcTrampoline = wndProc; user32WndProc.newWndProc = newWndProc; user32WndProc.procName = procName; user32WndProc.className = className; user32WndProc.isUnicode = isUnicode; - g_currentUser32WndProc = &user32WndProc; - if (isUnicode) - { - 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); - } + Compat::hookFunction(reinterpret_cast(user32WndProc.oldWndProcTrampoline), + user32WndProc.newWndProc, procName.c_str()); } template @@ -572,44 +544,29 @@ namespace } } +#define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc(className, #wndProcHook) +#define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW(className, #wndProcHook) + namespace Gdi { namespace User32WndProcs { void installHooks() { -#define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc(className, #wndProcHook) -#define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW(className, #wndProcHook) - - auto hook = SetWindowsHookEx(WH_CBT, cbtProc, nullptr, GetCurrentThreadId()); - HOOK_USER32_WNDPROC("Button", buttonWndProc); HOOK_USER32_WNDPROC("ComboBox", comboBoxWndProc); + HOOK_USER32_WNDPROC("ComboLBox", comboListBoxWndProc); HOOK_USER32_WNDPROC("Edit", editWndProc); HOOK_USER32_WNDPROC("ListBox", listBoxWndProc); HOOK_USER32_WNDPROC("MDIClient", mdiClientWndProc); + HOOK_USER32_WNDPROC("ScrollBar", scrollBarWndProc); HOOK_USER32_WNDPROC("Static", staticWndProc); - HOOK_USER32_WNDPROC("ComboLBox", comboListBoxWndProc); + HOOK_USER32_WNDPROC("#32768", menuWndProc); - 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); HOOK_FUNCTION(user32, DefDlgProcA, defDlgProcA); HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW); + HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA); + HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW); HOOK_FUNCTION(user32, SetMenuItemInfoW, setMenuItemInfoW); } }