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:
parent
2ae5287bcf
commit
f3718272cd
@ -24,7 +24,6 @@ namespace
|
|||||||
std::map<void*, HookedFunctionInfo> g_hookedFunctions;
|
std::map<void*, HookedFunctionInfo> g_hookedFunctions;
|
||||||
|
|
||||||
PIMAGE_NT_HEADERS getImageNtHeaders(HMODULE module);
|
PIMAGE_NT_HEADERS getImageNtHeaders(HMODULE module);
|
||||||
HMODULE getModuleHandleFromAddress(void* address);
|
|
||||||
std::filesystem::path getModulePath(HMODULE module);
|
std::filesystem::path getModulePath(HMODULE module);
|
||||||
|
|
||||||
std::map<void*, HookedFunctionInfo>::iterator findOrigFunc(void* origFunc)
|
std::map<void*, HookedFunctionInfo>::iterator findOrigFunc(void* origFunc)
|
||||||
@ -84,7 +83,7 @@ namespace
|
|||||||
std::string funcAddrToStr(void* funcPtr)
|
std::string funcAddrToStr(void* funcPtr)
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
HMODULE module = getModuleHandleFromAddress(funcPtr);
|
HMODULE module = Compat::getModuleHandleFromAddress(funcPtr);
|
||||||
oss << getModulePath(module).string() << "+0x" << std::hex <<
|
oss << getModulePath(module).string() << "+0x" << std::hex <<
|
||||||
reinterpret_cast<DWORD>(funcPtr) - reinterpret_cast<DWORD>(module);
|
reinterpret_cast<DWORD>(funcPtr) - reinterpret_cast<DWORD>(module);
|
||||||
return oss.str();
|
return oss.str();
|
||||||
@ -108,14 +107,6 @@ namespace
|
|||||||
return ntHeaders;
|
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)
|
std::filesystem::path getModulePath(HMODULE module)
|
||||||
{
|
{
|
||||||
char path[MAX_PATH] = {};
|
char path[MAX_PATH] = {};
|
||||||
@ -125,6 +116,22 @@ namespace
|
|||||||
|
|
||||||
void hookFunction(void*& origFuncPtr, void* newFuncPtr, const char* funcName)
|
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);
|
const auto it = findOrigFunc(origFuncPtr);
|
||||||
if (it != g_hookedFunctions.end())
|
if (it != g_hookedFunctions.end())
|
||||||
{
|
{
|
||||||
@ -140,11 +147,15 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* const hookedFuncPtr = origFuncPtr;
|
void* const hookedFuncPtr = origFuncPtr;
|
||||||
HMODULE module = nullptr;
|
if (stubFuncPtr)
|
||||||
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
{
|
||||||
static_cast<char*>(hookedFuncPtr), &module);
|
LOG_DEBUG << "Hooking function: " << funcName << " (" << funcAddrToStr(stubFuncPtr) << " -> "
|
||||||
|
<< funcAddrToStr(origFuncPtr) << ')';
|
||||||
LOG_DEBUG << "Hooking function: " << funcName << " (" << funcAddrToStr(hookedFuncPtr) << ')';
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_DEBUG << "Hooking function: " << funcName << " (" << funcAddrToStr(hookedFuncPtr) << ')';
|
||||||
|
}
|
||||||
|
|
||||||
DetourTransactionBegin();
|
DetourTransactionBegin();
|
||||||
const bool attachSuccessful = NO_ERROR == DetourAttach(&origFuncPtr, newFuncPtr);
|
const bool attachSuccessful = NO_ERROR == DetourAttach(&origFuncPtr, newFuncPtr);
|
||||||
@ -155,6 +166,9 @@ namespace
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HMODULE module = nullptr;
|
||||||
|
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<char*>(hookedFuncPtr), &module);
|
||||||
|
|
||||||
g_hookedFunctions.emplace(
|
g_hookedFunctions.emplace(
|
||||||
std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr }));
|
std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr }));
|
||||||
}
|
}
|
||||||
@ -175,6 +189,14 @@ namespace
|
|||||||
|
|
||||||
namespace Compat
|
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)
|
FARPROC getProcAddress(HMODULE module, const char* procName)
|
||||||
{
|
{
|
||||||
if (!module || !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);
|
return reinterpret_cast<FARPROC>(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
namespace Compat
|
namespace Compat
|
||||||
{
|
{
|
||||||
|
HMODULE getModuleHandleFromAddress(void* address);
|
||||||
|
|
||||||
template <typename OrigFuncPtr, OrigFuncPtr origFunc>
|
template <typename OrigFuncPtr, OrigFuncPtr origFunc>
|
||||||
OrigFuncPtr& getOrigFuncPtr()
|
OrigFuncPtr& getOrigFuncPtr()
|
||||||
{
|
{
|
||||||
|
@ -50,9 +50,6 @@ namespace
|
|||||||
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
|
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
|
||||||
LRESULT onSetText(HWND hwnd, WPARAM wParam, LPARAM lParam, 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)
|
LRESULT buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||||
{
|
{
|
||||||
switch (msg)
|
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)
|
LRESULT comboBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||||
{
|
{
|
||||||
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||||
@ -261,28 +225,36 @@ namespace
|
|||||||
void hookUser32WndProc(User32WndProc& user32WndProc, WNDPROC newWndProc,
|
void hookUser32WndProc(User32WndProc& user32WndProc, WNDPROC newWndProc,
|
||||||
const std::string& procName, const std::string& className, bool isUnicode)
|
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.newWndProc = newWndProc;
|
||||||
user32WndProc.procName = procName;
|
user32WndProc.procName = procName;
|
||||||
user32WndProc.className = className;
|
user32WndProc.className = className;
|
||||||
user32WndProc.isUnicode = isUnicode;
|
user32WndProc.isUnicode = isUnicode;
|
||||||
|
|
||||||
g_currentUser32WndProc = &user32WndProc;
|
Compat::hookFunction(reinterpret_cast<void*&>(user32WndProc.oldWndProcTrampoline),
|
||||||
if (isUnicode)
|
user32WndProc.newWndProc, procName.c_str());
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <WndProcHook wndProcHook>
|
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 Gdi
|
||||||
{
|
{
|
||||||
namespace User32WndProcs
|
namespace User32WndProcs
|
||||||
{
|
{
|
||||||
void installHooks()
|
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("Button", buttonWndProc);
|
||||||
HOOK_USER32_WNDPROC("ComboBox", comboBoxWndProc);
|
HOOK_USER32_WNDPROC("ComboBox", comboBoxWndProc);
|
||||||
|
HOOK_USER32_WNDPROC("ComboLBox", comboListBoxWndProc);
|
||||||
HOOK_USER32_WNDPROC("Edit", editWndProc);
|
HOOK_USER32_WNDPROC("Edit", editWndProc);
|
||||||
HOOK_USER32_WNDPROC("ListBox", listBoxWndProc);
|
HOOK_USER32_WNDPROC("ListBox", listBoxWndProc);
|
||||||
HOOK_USER32_WNDPROC("MDIClient", mdiClientWndProc);
|
HOOK_USER32_WNDPROC("MDIClient", mdiClientWndProc);
|
||||||
|
HOOK_USER32_WNDPROC("ScrollBar", scrollBarWndProc);
|
||||||
HOOK_USER32_WNDPROC("Static", staticWndProc);
|
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, DefDlgProcA, defDlgProcA);
|
||||||
HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW);
|
HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW);
|
||||||
|
HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA);
|
||||||
|
HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW);
|
||||||
HOOK_FUNCTION(user32, SetMenuItemInfoW, setMenuItemInfoW);
|
HOOK_FUNCTION(user32, SetMenuItemInfoW, setMenuItemInfoW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user