From 9ddeab41496c47eeb68998ac1d8cc5ffa5adb1de Mon Sep 17 00:00:00 2001 From: narzoul Date: Tue, 19 Apr 2016 23:57:35 +0200 Subject: [PATCH] Removed hooks on DLL_PROCESS_DETACH Fixes a crash when opening Blood 2's display settings from the launcher. --- DDrawCompat/CompatGdi.cpp | 14 +++++++ DDrawCompat/CompatGdi.h | 2 + DDrawCompat/CompatGdiCaret.cpp | 18 +++++--- DDrawCompat/CompatGdiCaret.h | 1 + DDrawCompat/CompatGdiDcFunctions.cpp | 4 -- DDrawCompat/CompatGdiPaintHandlers.cpp | 11 ++++- DDrawCompat/CompatGdiPaintHandlers.h | 1 + DDrawCompat/CompatGdiScrollFunctions.cpp | 2 - DDrawCompat/CompatGdiWinProc.cpp | 12 +++++- DDrawCompat/CompatGdiWinProc.h | 1 + DDrawCompat/CompatRegistry.cpp | 2 - DDrawCompat/CompatVtable.h | 2 - DDrawCompat/DllMain.cpp | 10 +++++ DDrawCompat/Hook.cpp | 52 +++++++++++++++++------- DDrawCompat/Hook.h | 5 +-- DDrawCompat/RealPrimarySurface.cpp | 25 ++++++++++++ DDrawCompat/RealPrimarySurface.h | 1 + 17 files changed, 126 insertions(+), 37 deletions(-) diff --git a/DDrawCompat/CompatGdi.cpp b/DDrawCompat/CompatGdi.cpp index 025d934..74dcfc5 100644 --- a/DDrawCompat/CompatGdi.cpp +++ b/DDrawCompat/CompatGdi.cpp @@ -196,6 +196,20 @@ namespace CompatGdi return RealPrimarySurface::isFullScreen(); } + void unhookWndProc(LPCSTR className, WNDPROC oldWndProc) + { + HWND hwnd = CreateWindow(className, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, 0); + SetClassLongPtr(hwnd, GCLP_WNDPROC, reinterpret_cast(oldWndProc)); + DestroyWindow(hwnd); + } + + void uninstallHooks() + { + CompatGdiCaret::uninstallHooks(); + CompatGdiWinProc::uninstallHooks(); + CompatGdiPaintHandlers::uninstallHooks(); + } + void updatePalette(DWORD startingEntry, DWORD count) { if (isEmulationEnabled() && CompatPrimarySurface::palette) diff --git a/DDrawCompat/CompatGdi.h b/DDrawCompat/CompatGdi.h index ddddc8e..3deb5df 100644 --- a/DDrawCompat/CompatGdi.h +++ b/DDrawCompat/CompatGdi.h @@ -13,6 +13,8 @@ namespace CompatGdi void installHooks(); void invalidate(const RECT* rect); bool isEmulationEnabled(); + void unhookWndProc(LPCSTR className, WNDPROC oldWndProc); + void uninstallHooks(); void updatePalette(DWORD startingEntry, DWORD count); extern CRITICAL_SECTION g_gdiCriticalSection; diff --git a/DDrawCompat/CompatGdiCaret.cpp b/DDrawCompat/CompatGdiCaret.cpp index dbe15e2..48b6c8a 100644 --- a/DDrawCompat/CompatGdiCaret.cpp +++ b/DDrawCompat/CompatGdiCaret.cpp @@ -10,6 +10,9 @@ namespace { + HWINEVENTHOOK g_compatGdiCaretGeneralEventHook = nullptr; + HWINEVENTHOOK g_compatGdiCaretLocationChangeEventHook = nullptr; + template using FuncPtr = Result(WINAPI *)(Params...); @@ -121,18 +124,23 @@ namespace CompatGdiCaret { InitializeCriticalSection(&g_caretCriticalSection); - Compat::beginHookTransaction(); HOOK_GDI_CARET_FUNCTION(user32, CreateCaret); HOOK_GDI_CARET_FUNCTION(user32, DestroyCaret); HOOK_GDI_CARET_FUNCTION(user32, HideCaret); HOOK_GDI_CARET_FUNCTION(user32, SetCaretPos); HOOK_GDI_CARET_FUNCTION(user32, ShowCaret); - Compat::endHookTransaction(); const DWORD threadId = GetCurrentThreadId(); - SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_HIDE, - nullptr, &compatGdiCaretEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); - SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, + g_compatGdiCaretGeneralEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_HIDE, nullptr, &compatGdiCaretEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); + g_compatGdiCaretLocationChangeEventHook = SetWinEventHook( + EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, nullptr, &compatGdiCaretEvent, + 0, threadId, WINEVENT_OUTOFCONTEXT); + } + + void uninstallHooks() + { + UnhookWinEvent(g_compatGdiCaretLocationChangeEventHook); + UnhookWinEvent(g_compatGdiCaretGeneralEventHook); } } diff --git a/DDrawCompat/CompatGdiCaret.h b/DDrawCompat/CompatGdiCaret.h index 8346199..d8de8ec 100644 --- a/DDrawCompat/CompatGdiCaret.h +++ b/DDrawCompat/CompatGdiCaret.h @@ -3,4 +3,5 @@ namespace CompatGdiCaret { void installHooks(); + void uninstallHooks(); } diff --git a/DDrawCompat/CompatGdiDcFunctions.cpp b/DDrawCompat/CompatGdiDcFunctions.cpp index 9a21025..e363069 100644 --- a/DDrawCompat/CompatGdiDcFunctions.cpp +++ b/DDrawCompat/CompatGdiDcFunctions.cpp @@ -135,8 +135,6 @@ namespace CompatGdiDcFunctions { void installHooks() { - Compat::beginHookTransaction(); - // Bitmap functions HOOK_GDI_DC_FUNCTION(msimg32, AlphaBlend); HOOK_GDI_DC_FUNCTION(gdi32, BitBlt); @@ -226,7 +224,5 @@ namespace CompatGdiDcFunctions // Undocumented functions HOOK_GDI_DC_FUNCTION(gdi32, GdiDrawStream); HOOK_GDI_DC_FUNCTION(gdi32, PolyPatBlt); - - Compat::endHookTransaction(); } } diff --git a/DDrawCompat/CompatGdiPaintHandlers.cpp b/DDrawCompat/CompatGdiPaintHandlers.cpp index 986e9c9..904e18e 100644 --- a/DDrawCompat/CompatGdiPaintHandlers.cpp +++ b/DDrawCompat/CompatGdiPaintHandlers.cpp @@ -316,11 +316,18 @@ namespace CompatGdiPaintHandlers CompatGdi::hookWndProc("#32768", g_origMenuWndProc, &menuWndProc); CompatGdi::hookWndProc("ScrollBar", g_origScrollBarWndProc, &scrollBarWndProc); - Compat::beginHookTransaction(); HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA); HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW); HOOK_FUNCTION(user32, DefDlgProcA, defDlgProcA); HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW); - Compat::endHookTransaction(); + } + + void uninstallHooks() + { + CompatGdi::unhookWndProc("ComboLBox", g_origComboListBoxWndProc); + CompatGdi::unhookWndProc("Edit", g_origEditWndProc); + CompatGdi::unhookWndProc("ListBox", g_origListBoxWndProc); + CompatGdi::unhookWndProc("#32768", g_origMenuWndProc); + CompatGdi::unhookWndProc("ScrollBar", g_origScrollBarWndProc); } } diff --git a/DDrawCompat/CompatGdiPaintHandlers.h b/DDrawCompat/CompatGdiPaintHandlers.h index 74cce84..45681b4 100644 --- a/DDrawCompat/CompatGdiPaintHandlers.h +++ b/DDrawCompat/CompatGdiPaintHandlers.h @@ -3,4 +3,5 @@ namespace CompatGdiPaintHandlers { void installHooks(); + void uninstallHooks(); } diff --git a/DDrawCompat/CompatGdiScrollFunctions.cpp b/DDrawCompat/CompatGdiScrollFunctions.cpp index 292a1cf..1e202d2 100644 --- a/DDrawCompat/CompatGdiScrollFunctions.cpp +++ b/DDrawCompat/CompatGdiScrollFunctions.cpp @@ -47,10 +47,8 @@ namespace CompatGdiScrollFunctions { void installHooks() { - Compat::beginHookTransaction(); HOOK_FUNCTION(user32, ScrollWindow, scrollWindow); HOOK_FUNCTION(user32, ScrollWindowEx, scrollWindowEx); - Compat::endHookTransaction(); } void updateScrolledWindow(HWND hwnd) diff --git a/DDrawCompat/CompatGdiWinProc.cpp b/DDrawCompat/CompatGdiWinProc.cpp index 89b1d6d..288c2e1 100644 --- a/DDrawCompat/CompatGdiWinProc.cpp +++ b/DDrawCompat/CompatGdiWinProc.cpp @@ -16,6 +16,8 @@ namespace { + HHOOK g_callWndRetProcHook = nullptr; + HWINEVENTHOOK g_objectStateChangeEventHook = nullptr; std::unordered_map g_prevWindowRect; void disableDwmAttributes(HWND hwnd); @@ -196,8 +198,14 @@ namespace CompatGdiWinProc void installHooks() { const DWORD threadId = GetCurrentThreadId(); - SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); - SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE, + g_callWndRetProcHook = SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); + g_objectStateChangeEventHook = SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE, nullptr, &objectStateChangeEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); } + + void uninstallHooks() + { + UnhookWinEvent(g_objectStateChangeEventHook); + UnhookWindowsHookEx(g_callWndRetProcHook); + } } diff --git a/DDrawCompat/CompatGdiWinProc.h b/DDrawCompat/CompatGdiWinProc.h index a291106..0d87e3a 100644 --- a/DDrawCompat/CompatGdiWinProc.h +++ b/DDrawCompat/CompatGdiWinProc.h @@ -3,4 +3,5 @@ namespace CompatGdiWinProc { void installHooks(); + void uninstallHooks(); } diff --git a/DDrawCompat/CompatRegistry.cpp b/DDrawCompat/CompatRegistry.cpp index 4652702..fe05cae 100644 --- a/DDrawCompat/CompatRegistry.cpp +++ b/DDrawCompat/CompatRegistry.cpp @@ -90,9 +90,7 @@ namespace CompatRegistry { void installHooks() { - Compat::beginHookTransaction(); HOOK_FUNCTION(KernelBase, RegGetValueW, regGetValueW); - Compat::endHookTransaction(); } void setValue(HKEY key, const char* subKey, const char* valueName, DWORD value) diff --git a/DDrawCompat/CompatVtable.h b/DDrawCompat/CompatVtable.h index f1fabad..db7c32f 100644 --- a/DDrawCompat/CompatVtable.h +++ b/DDrawCompat/CompatVtable.h @@ -114,9 +114,7 @@ private: s_vtablePtrToCompatVtable[s_vtablePtr] = &s_compatVtable; Compat::g_hookedMethods.emplace(origMethodPtr, Compat::HookedMethodInfo(origMethodPtr, s_vtablePtrToCompatVtable)); - Compat::beginHookTransaction(); Compat::hookFunction(origMethodPtr, newMethodPtr); - Compat::endHookTransaction(); } } diff --git a/DDrawCompat/DllMain.cpp b/DDrawCompat/DllMain.cpp index 18118e4..db3878d 100644 --- a/DDrawCompat/DllMain.cpp +++ b/DDrawCompat/DllMain.cpp @@ -13,6 +13,7 @@ #include "CompatVtable.h" #include "DDrawProcs.h" #include "DDrawRepository.h" +#include "RealPrimarySurface.h" #include "Time.h" struct IDirectInput; @@ -142,6 +143,10 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) { if (fdwReason == DLL_PROCESS_ATTACH) { + char currentProcessPath[MAX_PATH] = {}; + GetModuleFileName(nullptr, currentProcessPath, MAX_PATH); + Compat::Log() << "Process path: " << currentProcessPath; + char currentDllPath[MAX_PATH] = {}; GetModuleFileName(hinstDLL, currentDllPath, MAX_PATH); Compat::Log() << "Loading DDrawCompat from " << currentDllPath; @@ -181,8 +186,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) } else if (fdwReason == DLL_PROCESS_DETACH) { + Compat::Log() << "Detaching DDrawCompat"; + RealPrimarySurface::removeUpdateThread(); + CompatGdi::uninstallHooks(); + Compat::unhookAllFunctions(); FreeLibrary(g_origDInputModule); FreeLibrary(g_origDDrawModule); + Compat::Log() << "DDrawCompat detached successfully"; } return TRUE; diff --git a/DDrawCompat/Hook.cpp b/DDrawCompat/Hook.cpp index 2f9d05f..22e3484 100644 --- a/DDrawCompat/Hook.cpp +++ b/DDrawCompat/Hook.cpp @@ -1,5 +1,8 @@ #define WIN32_LEAN_AND_MEAN +#include +#include + #include #include @@ -8,6 +11,8 @@ namespace { + std::vector> g_hookedFunctions; + FARPROC getProcAddress(HMODULE module, const char* procName) { if (!module || !procName) @@ -45,25 +50,36 @@ namespace return nullptr; } + + void hookFunction(const char* funcName, void*& origFuncPtr, void* newFuncPtr) + { + DetourTransactionBegin(); + const bool attachSuccessful = NO_ERROR == DetourAttach(&origFuncPtr, newFuncPtr); + const bool commitSuccessful = NO_ERROR == DetourTransactionCommit(); + if (!attachSuccessful || !commitSuccessful) + { + if (funcName) + { + Compat::Log() << "Failed to hook a function: " << funcName; + } + else + { + Compat::Log() << "Failed to hook a function: " << origFuncPtr; + } + return; + } + + g_hookedFunctions.push_back(std::make_pair(origFuncPtr, newFuncPtr)); + } } namespace Compat { - void beginHookTransaction() - { - DetourTransactionBegin(); - } - - void endHookTransaction() - { - DetourTransactionCommit(); - } - void hookFunction(void*& origFuncPtr, void* newFuncPtr) { - DetourAttach(&origFuncPtr, newFuncPtr); + ::hookFunction(nullptr, origFuncPtr, newFuncPtr); } - + void hookFunction(const char* moduleName, const char* funcName, void*& origFuncPtr, void* newFuncPtr) { FARPROC procAddr = getProcAddress(GetModuleHandle(moduleName), funcName); @@ -74,10 +90,16 @@ namespace Compat } origFuncPtr = procAddr; - if (NO_ERROR != DetourAttach(&origFuncPtr, newFuncPtr)) + ::hookFunction(funcName, origFuncPtr, newFuncPtr); + } + + void unhookAllFunctions() + { + for (auto& hookedFunc : g_hookedFunctions) { - Compat::Log() << "Failed to hook a function: " << funcName; - return; + DetourTransactionBegin(); + DetourDetach(&hookedFunc.first, hookedFunc.second); + DetourTransactionCommit(); } } } \ No newline at end of file diff --git a/DDrawCompat/Hook.h b/DDrawCompat/Hook.h index 2d41e2e..b9440e2 100644 --- a/DDrawCompat/Hook.h +++ b/DDrawCompat/Hook.h @@ -7,9 +7,6 @@ namespace Compat { - void beginHookTransaction(); - void endHookTransaction(); - template OrigFuncPtr& getOrigFuncPtr() { @@ -26,4 +23,6 @@ namespace Compat hookFunction(moduleName, funcName, reinterpret_cast(getOrigFuncPtr()), newFuncPtr); } + + void unhookAllFunctions(); } diff --git a/DDrawCompat/RealPrimarySurface.cpp b/DDrawCompat/RealPrimarySurface.cpp index 0360f2f..499423f 100644 --- a/DDrawCompat/RealPrimarySurface.cpp +++ b/DDrawCompat/RealPrimarySurface.cpp @@ -24,6 +24,7 @@ namespace IDirectDrawSurface7* g_backBuffer = nullptr; IReleaseNotifier g_releaseNotifier(onRelease); + bool g_stopUpdateThread = false; HANDLE g_updateThread = nullptr; HANDLE g_updateEvent = nullptr; RECT g_updateRect = {}; @@ -140,6 +141,11 @@ namespace { WaitForSingleObject(g_updateEvent, INFINITE); + if (g_stopUpdateThread) + { + return 0; + } + const long long qpcTargetNextUpdate = g_qpcNextUpdate; const int msUntilNextUpdate = Time::qpcToMs(qpcTargetNextUpdate - Time::queryPerformanceCounter()); @@ -317,6 +323,25 @@ void RealPrimarySurface::release() } } +void RealPrimarySurface::removeUpdateThread() +{ + if (!g_updateThread) + { + return; + } + + g_stopUpdateThread = true; + SetEvent(g_updateEvent); + if (WAIT_OBJECT_0 != WaitForSingleObject(g_updateThread, 1000)) + { + TerminateThread(g_updateThread, 0); + Compat::Log() << "The update thread was terminated forcefully"; + } + ResetEvent(g_updateEvent); + g_stopUpdateThread = false; + g_updateThread = nullptr; +} + HRESULT RealPrimarySurface::restore() { return g_frontBuffer->lpVtbl->Restore(g_frontBuffer); diff --git a/DDrawCompat/RealPrimarySurface.h b/DDrawCompat/RealPrimarySurface.h index 33f1a9f..ac2e18c 100644 --- a/DDrawCompat/RealPrimarySurface.h +++ b/DDrawCompat/RealPrimarySurface.h @@ -18,6 +18,7 @@ public: static bool isFullScreen(); static bool isLost(); static void release(); + static void removeUpdateThread(); static HRESULT restore(); static void setClipper(LPDIRECTDRAWCLIPPER clipper); static void setPalette();