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
{
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<User32WndProc> g_user32WndProcA;
std::vector<User32WndProc> g_user32WndProcW;
std::vector<WndProcHook> g_user32WndProcHook;
User32WndProc* g_currentUser32WndProc = nullptr;
std::vector<std::string> 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<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)
{
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>& user32WndProc)
template <WndProcHook>
User32WndProc& getUser32WndProcA()
{
User32WndProc wndProc;
wndProc.oldWndProc =
reinterpret_cast<WNDPROC>(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<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(
reinterpret_cast<void*&>(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 <WndProcHook wndProcHook>
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<wndProcHook>(), user32WndProcA<wndProcHook>,
procName + 'A', className, false);
}
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 = {};
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<wndProcHook>(), user32WndProcW<wndProcHook>,
procName + 'W', className, true);
}
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)
@ -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 <int index>
template <WndProcHook wndProcHook>
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)
{
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();
#define HOOK_USER32_WNDPROC(index, name, wndProcHook) \
g_user32WndProcHook.push_back(wndProcHook); \
hookUser32WndProcA(name, user32WndProcA<index>, #wndProcHook); \
hookUser32WndProcW(name, user32WndProcW<index>, #wndProcHook)
#define HOOK_USER32_WNDPROC(className, wndProcHook) hookUser32WndProc<wndProcHook>(className, #wndProcHook)
#define HOOK_USER32_WNDPROCW(className, wndProcHook) hookUser32WndProcW<wndProcHook>(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<WNDPROC>(GetWindowLongA(hwnd, GWL_WNDPROC));
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)
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<WNDPROC>(GetWindowLongW(hwnd, GWL_WNDPROC));
User32WndProc* user32WndProc = nullptr;
if (getUser32WndProcW<menuWndProc>().oldWndProc == wndProc)
{
if (IsWindowUnicode(hwnd))
{
CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC,
reinterpret_cast<LONG>(g_user32WndProcW[index].newWndProc));
}
else
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC,
reinterpret_cast<LONG>(g_user32WndProcA[index].newWndProc));
}
user32WndProc = &getUser32WndProcW<menuWndProc>();
}
else if (getUser32WndProcW<scrollBarWndProc>().oldWndProc == wndProc)
{
user32WndProc = &getUser32WndProcW<scrollBarWndProc>();
}
if (user32WndProc)
{
SetWindowLongW(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(user32WndProc->newWndProc));
}
}
}