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

Improved hooking of user32 window procedures

This commit is contained in:
narzoul 2019-09-01 15:45:36 +02:00
parent c7b94700d3
commit 81c2748027

View File

@ -21,17 +21,12 @@ namespace
struct User32WndProc struct User32WndProc
{ {
WNDPROC oldWndProcTrampoline;
WNDPROC oldWndProc; WNDPROC oldWndProc;
WNDPROC oldWndProcTrampoline;
WNDPROC newWndProc; WNDPROC newWndProc;
std::string name; std::string procName;
std::string className;
User32WndProc() bool isUnicode;
: oldWndProcTrampoline(nullptr)
, oldWndProc(nullptr)
, newWndProc(nullptr)
{
}
}; };
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc); LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc);
@ -42,12 +37,8 @@ namespace
LRESULT onPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
int g_menuWndProcIndex = 0; User32WndProc* g_currentUser32WndProc = nullptr;
int g_scrollBarWndProcIndex = 0; std::vector<std::string> g_failedHooks;
std::vector<User32WndProc> g_user32WndProcA;
std::vector<User32WndProc> g_user32WndProcW;
std::vector<WndProcHook> g_user32WndProcHook;
LRESULT buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) 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<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 ? GetWindowLongW : 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);
}
}
}
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);
@ -163,39 +186,66 @@ namespace
return result; return result;
} }
void hookUser32WndProc(const std::string& wndProcName, HWND hwnd, WNDPROC newWndProc, template <WndProcHook>
decltype(GetWindowLongPtr)* getWindowLong, std::vector<User32WndProc>& user32WndProc) User32WndProc& getUser32WndProcA()
{ {
User32WndProc wndProc; static User32WndProc user32WndProcA = {};
wndProc.oldWndProc = return user32WndProcA;
reinterpret_cast<WNDPROC>(getWindowLong(hwnd, GWL_WNDPROC)); }
wndProc.oldWndProcTrampoline = wndProc.oldWndProc;
wndProc.newWndProc = newWndProc;
wndProc.name = wndProcName;
user32WndProc.push_back(wndProc);
if (reinterpret_cast<DWORD>(wndProc.oldWndProcTrampoline) < 0xFFFF0000) template <WndProcHook>
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( DestroyWindow(CreateWindowW(std::wstring(className.begin(), className.end()).c_str(), L"", 0, 0, 0, 0, 0,
reinterpret_cast<void*&>(user32WndProc.back().oldWndProcTrampoline), newWndProc); 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 <WndProcHook wndProcHook>
void hookUser32WndProcA(const std::string& className, const std::string& procName)
{ {
CLIENTCREATESTRUCT ccs = {}; hookUser32WndProc(getUser32WndProcA<wndProcHook>(), user32WndProcA<wndProcHook>,
HWND hwnd = CreateWindowA(className, "", 0, 0, 0, 0, 0, 0, 0, 0, &ccs); procName + 'A', className, false);
hookUser32WndProc(wndProcName + 'A', hwnd, newWndProc, CALL_ORIG_FUNC(GetWindowLongA), g_user32WndProcA);
DestroyWindow(hwnd);
} }
void hookUser32WndProcW(const char* name, WNDPROC newWndProc, const std::string& wndProcName) template <WndProcHook wndProcHook>
void hookUser32WndProcW(const std::string& className, const std::string& procName)
{ {
CLIENTCREATESTRUCT ccs = {}; hookUser32WndProc(getUser32WndProcW<wndProcHook>(), user32WndProcW<wndProcHook>,
HWND hwnd = CreateWindowW( procName + 'W', className, true);
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); template <WndProcHook wndProcHook>
void hookUser32WndProc(const std::string& className, const std::string& procName)
{
hookUser32WndProcA<wndProcHook>(className, procName);
hookUser32WndProcW<wndProcHook>(className, procName);
} }
LRESULT listBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) 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, 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)); LOG_FUNC(procName.c_str(), hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam));
return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, user32WndProc.oldWndProcTrampoline)); return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline));
} }
template <int index> template <WndProcHook wndProcHook>
LRESULT CALLBACK user32WndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 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<wndProcHook>();
return user32WndProc(hwnd, uMsg, wParam, lParam, wp.procName, wndProcHook, wp.oldWndProcTrampoline);
} }
template <int index> template <WndProcHook wndProcHook>
LRESULT CALLBACK user32WndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 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<wndProcHook>();
return user32WndProc(hwnd, uMsg, wParam, lParam, wp.procName, wndProcHook, wp.oldWndProcTrampoline);
} }
} }
@ -396,28 +448,33 @@ namespace Gdi
{ {
disableImmersiveContextMenus(); disableImmersiveContextMenus();
#define HOOK_USER32_WNDPROC(index, name, wndProcHook) \ #define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc<wndProcHook>(className, #wndProcHook)
g_user32WndProcHook.push_back(wndProcHook); \ #define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW<wndProcHook>(className, #wndProcHook)
hookUser32WndProcA(name, user32WndProcA<index>, #wndProcHook); \
hookUser32WndProcW(name, user32WndProcW<index>, #wndProcHook)
g_user32WndProcA.reserve(9); auto hook = SetWindowsHookEx(WH_CBT, cbtProc, nullptr, GetCurrentThreadId());
g_user32WndProcW.reserve(9);
HOOK_USER32_WNDPROC(0, "Button", buttonWndProc); HOOK_USER32_WNDPROC("Button", buttonWndProc);
HOOK_USER32_WNDPROC(1, "ComboBox", comboBoxWndProc); HOOK_USER32_WNDPROC("ComboBox", comboBoxWndProc);
HOOK_USER32_WNDPROC(2, "Edit", editWndProc); HOOK_USER32_WNDPROC("Edit", editWndProc);
HOOK_USER32_WNDPROC(3, "ListBox", listBoxWndProc); HOOK_USER32_WNDPROC("ListBox", listBoxWndProc);
HOOK_USER32_WNDPROC(4, "MDIClient", mdiClientWndProc); HOOK_USER32_WNDPROC("MDIClient", mdiClientWndProc);
HOOK_USER32_WNDPROC(5, "ScrollBar", scrollBarWndProc); HOOK_USER32_WNDPROC("Static", staticWndProc);
HOOK_USER32_WNDPROC(6, "Static", staticWndProc); HOOK_USER32_WNDPROC("ComboLBox", comboListBoxWndProc);
HOOK_USER32_WNDPROC(7, "ComboLBox", comboListBoxWndProc);
HOOK_USER32_WNDPROC(8, "#32768", menuWndProc);
g_scrollBarWndProcIndex = 5; HOOK_USER32_WNDPROCW("ScrollBar", scrollBarWndProc);
g_menuWndProcIndex = 8; HOOK_USER32_WNDPROCW("#32768", menuWndProc);
UnhookWindowsHookEx(hook);
#undef HOOK_USER32_WNDPROC #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, DefWindowProcA, defWindowProcA);
HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW); HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW);
@ -427,33 +484,25 @@ namespace Gdi
void onCreateWindow(HWND hwnd) void onCreateWindow(HWND hwnd)
{ {
WNDPROC wndProcA = reinterpret_cast<WNDPROC>(GetWindowLongA(hwnd, GWL_WNDPROC)); if (!IsWindowUnicode(hwnd))
WNDPROC wndProcW = reinterpret_cast<WNDPROC>(GetWindowLongW(hwnd, GWL_WNDPROC));
int index = -1;
if (wndProcA == g_user32WndProcA[g_menuWndProcIndex].oldWndProc ||
wndProcW == g_user32WndProcW[g_menuWndProcIndex].oldWndProc)
{ {
index = g_menuWndProcIndex; return;
}
else if (wndProcA == g_user32WndProcA[g_scrollBarWndProcIndex].oldWndProc ||
wndProcW == g_user32WndProcW[g_scrollBarWndProcIndex].oldWndProc)
{
index = g_scrollBarWndProcIndex;
} }
if (-1 != index) const auto wndProc = reinterpret_cast<WNDPROC>(GetWindowLongW(hwnd, GWL_WNDPROC));
User32WndProc* user32WndProc = nullptr;
if (getUser32WndProcW<menuWndProc>().oldWndProc == wndProc)
{ {
if (IsWindowUnicode(hwnd)) user32WndProc = &getUser32WndProcW<menuWndProc>();
{ }
CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC, else if (getUser32WndProcW<scrollBarWndProc>().oldWndProc == wndProc)
reinterpret_cast<LONG>(g_user32WndProcW[index].newWndProc)); {
} user32WndProc = &getUser32WndProcW<scrollBarWndProc>();
else }
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, if (user32WndProc)
reinterpret_cast<LONG>(g_user32WndProcA[index].newWndProc)); {
} SetWindowLongW(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(user32WndProc->newWndProc));
} }
} }
} }