1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Hook ASCII window procedures for menus and scroll bars

Fixes missing texts in menus of PGA Championship Golf 2000 (issue #84).
This commit is contained in:
narzoul 2021-01-04 21:21:05 +01:00
parent 2ae5287bcf
commit f3718272cd
3 changed files with 72 additions and 103 deletions

View File

@ -24,7 +24,6 @@ namespace
std::map<void*, HookedFunctionInfo> g_hookedFunctions;
PIMAGE_NT_HEADERS getImageNtHeaders(HMODULE module);
HMODULE getModuleHandleFromAddress(void* address);
std::filesystem::path getModulePath(HMODULE module);
std::map<void*, HookedFunctionInfo>::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<DWORD>(funcPtr) - reinterpret_cast<DWORD>(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<char*>(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<BYTE*>(origFuncPtr)[0] &&
0x25 == reinterpret_cast<BYTE*>(origFuncPtr)[1])
{
void* jmpTarget = **reinterpret_cast<void***>(reinterpret_cast<BYTE*>(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<char*>(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<char*>(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<char*>(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<BYTE>(func[0]) &&
0x25 == static_cast<BYTE>(func[1]))
{
FARPROC jmpTarget = **reinterpret_cast<FARPROC**>(func + 2);
if (getModuleHandleFromAddress(jmpTarget) == module)
{
return jmpTarget;
}
}
return reinterpret_cast<FARPROC>(func);
}

View File

@ -13,6 +13,8 @@
namespace Compat
{
HMODULE getModuleHandleFromAddress(void* address);
template <typename OrigFuncPtr, OrigFuncPtr origFunc>
OrigFuncPtr& getOrigFuncPtr()
{

View File

@ -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<std::string> 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<HWND>(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<WNDPROC>(getWindowLong(hwnd, GWL_WNDPROC));
HMODULE wndProcModule = nullptr;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<const char*>(wndProc), &wndProcModule);
if (wndProcModule && wndProcModule == GetModuleHandle("user32"))
{
g_currentUser32WndProc->oldWndProc = wndProc;
g_currentUser32WndProc->oldWndProcTrampoline = wndProc;
Compat::hookFunction(reinterpret_cast<void*&>(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<void*&>(user32WndProc.oldWndProcTrampoline),
user32WndProc.newWndProc, procName.c_str());
}
template <WndProcHook wndProcHook>
@ -572,44 +544,29 @@ namespace
}
}
#define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc<wndProcHook>(className, #wndProcHook)
#define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW<wndProcHook>(className, #wndProcHook)
namespace Gdi
{
namespace User32WndProcs
{
void installHooks()
{
#define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc<wndProcHook>(className, #wndProcHook)
#define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW<wndProcHook>(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);
}
}