From 58aba66ca2c97ebab1e94bb886a10180c41c1fb8 Mon Sep 17 00:00:00 2001 From: narzoul Date: Fri, 26 Oct 2018 14:05:25 +0200 Subject: [PATCH] Hook all GDI threads --- DDrawCompat/Common/ScopedCriticalSection.h | 14 +++ DDrawCompat/D3dDdi/KernelModeThunks.cpp | 3 +- DDrawCompat/DDraw/DirectDraw.cpp | 10 --- DDrawCompat/DDraw/RealPrimarySurface.cpp | 9 +- .../DDraw/Surfaces/PrimarySurfaceImpl.cpp | 1 - DDrawCompat/DDraw/Surfaces/TagSurface.cpp | 48 ---------- DDrawCompat/DDraw/Surfaces/TagSurface.h | 15 ---- DDrawCompat/DDrawCompat.vcxproj | 2 - DDrawCompat/DDrawCompat.vcxproj.filters | 6 -- DDrawCompat/Dll/DllMain.cpp | 26 ++++-- DDrawCompat/Gdi/Caret.cpp | 1 - DDrawCompat/Gdi/Dc.cpp | 56 ++++++++---- DDrawCompat/Gdi/Dc.h | 2 + DDrawCompat/Gdi/DcCache.cpp | 69 +++++++++++--- DDrawCompat/Gdi/DcCache.h | 2 + DDrawCompat/Gdi/DcFunctions.cpp | 66 ++++++++------ DDrawCompat/Gdi/Gdi.cpp | 37 ++++---- DDrawCompat/Gdi/Gdi.h | 2 +- DDrawCompat/Gdi/PaintHandlers.cpp | 80 ++++++----------- DDrawCompat/Gdi/PaintHandlers.h | 2 +- DDrawCompat/Gdi/VirtualScreen.cpp | 22 +++-- DDrawCompat/Gdi/WinProc.cpp | 90 ++++++++++++++++--- DDrawCompat/Gdi/WinProc.h | 1 + DDrawCompat/Gdi/Window.cpp | 21 +++-- DDrawCompat/Gdi/Window.h | 1 + DDrawCompat/Win32/DisplayMode.cpp | 9 +- 26 files changed, 334 insertions(+), 261 deletions(-) delete mode 100644 DDrawCompat/DDraw/Surfaces/TagSurface.cpp delete mode 100644 DDrawCompat/DDraw/Surfaces/TagSurface.h diff --git a/DDrawCompat/Common/ScopedCriticalSection.h b/DDrawCompat/Common/ScopedCriticalSection.h index e3aff9f..b31418a 100644 --- a/DDrawCompat/Common/ScopedCriticalSection.h +++ b/DDrawCompat/Common/ScopedCriticalSection.h @@ -6,6 +6,20 @@ namespace Compat { + class CriticalSection : public CRITICAL_SECTION + { + public: + CriticalSection() + { + InitializeCriticalSection(this); + } + + ~CriticalSection() + { + DeleteCriticalSection(this); + } + }; + class ScopedCriticalSection { public: diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp index e424da7..d8ae05c 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.cpp +++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp @@ -39,7 +39,7 @@ namespace D3DKMT_HANDLE g_lastPresentContext = 0; UINT g_presentCount = 0; std::atomic g_qpcLastVerticalBlank = 0; - CRITICAL_SECTION g_vblankCs = {}; + Compat::CriticalSection g_vblankCs; decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr; @@ -313,7 +313,6 @@ namespace D3dDdi void installHooks() { - InitializeCriticalSection(&g_vblankCs); HOOK_FUNCTION(gdi32, D3DKMTCloseAdapter, closeAdapter); HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext); HOOK_FUNCTION(gdi32, D3DKMTCreateDevice, createDevice); diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index 84491ab..c5b7783 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -3,7 +3,6 @@ #include "DDraw/ActivateAppHandler.h" #include "DDraw/DirectDraw.h" #include "DDraw/Repository.h" -#include "DDraw/Surfaces/TagSurface.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "Win32/DisplayMode.h" @@ -182,15 +181,6 @@ namespace DDraw HRESULT result = s_origVtable.SetCooperativeLevel(This, hWnd, dwFlags); if (SUCCEEDED(result)) { - void* ddObject = getDdObject(*This); - TagSurface* tagSurface = TagSurface::get(ddObject); - if (!tagSurface) - { - CompatPtr dd(Compat::queryInterface(This)); - TagSurface::create(*dd); - tagSurface = TagSurface::get(ddObject); - } - ActivateAppHandler::setCooperativeLevel(hWnd, dwFlags); } return result; diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index 3581bf3..ede3c3f 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -50,7 +50,11 @@ namespace BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam) { - if (IsWindowVisible(hwnd) && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && + DWORD windowPid = 0; + GetWindowThreadProcessId(hwnd, &windowPid); + if (GetCurrentProcessId() == windowPid && + IsWindowVisible(hwnd) && + (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && !Gdi::Window::isPresentationWindow(hwnd)) { auto& visibleLayeredWindows = *reinterpret_cast*>(lParam); @@ -119,8 +123,7 @@ namespace void bltVisibleLayeredWindowsToBackBuffer() { std::vector visibleLayeredWindows; - EnumThreadWindows(Gdi::getGdiThreadId(), addVisibleLayeredWindowToVector, - reinterpret_cast(&visibleLayeredWindows)); + EnumWindows(addVisibleLayeredWindowToVector, reinterpret_cast(&visibleLayeredWindows)); if (visibleLayeredWindows.empty()) { diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index d813d52..cb785b4 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -151,7 +151,6 @@ namespace DDraw if (SUCCEEDED(result)) { PrimarySurface::onRestore(); - Gdi::redraw(nullptr); } } } diff --git a/DDrawCompat/DDraw/Surfaces/TagSurface.cpp b/DDrawCompat/DDraw/Surfaces/TagSurface.cpp deleted file mode 100644 index 3a4303c..0000000 --- a/DDrawCompat/DDraw/Surfaces/TagSurface.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -#include "DDraw/DirectDraw.h" -#include "DDraw/Repository.h" -#include "DDraw/Surfaces/TagSurface.h" - -namespace -{ - std::map g_tagSurfaces; -} - -namespace DDraw -{ - TagSurface::~TagSurface() - { - Repository::onRelease(m_ddObject); - g_tagSurfaces.erase(std::find_if(g_tagSurfaces.begin(), g_tagSurfaces.end(), - [=](auto& i) { return i.second == this; })); - } - - HRESULT TagSurface::create(CompatRef dd) - { - DDSURFACEDESC desc = {}; - desc.dwSize = sizeof(desc); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; - desc.dwWidth = 1; - desc.dwHeight = 1; - desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; - - IDirectDrawSurface* surface = nullptr; - HRESULT result = Surface::create(dd, desc, surface); - if (SUCCEEDED(result)) - { - std::unique_ptr privateData(new TagSurface()); - g_tagSurfaces[getDdObject(dd.get())] = static_cast(privateData.get()); - CompatPtr surface7(Compat::queryInterface(surface)); - attach(*surface7, privateData); - } - return result; - } - - TagSurface* TagSurface::get(void* ddObject) - { - auto it = g_tagSurfaces.find(ddObject); - return it != g_tagSurfaces.end() ? it->second : nullptr; - } -} diff --git a/DDrawCompat/DDraw/Surfaces/TagSurface.h b/DDrawCompat/DDraw/Surfaces/TagSurface.h deleted file mode 100644 index 29d4194..0000000 --- a/DDrawCompat/DDraw/Surfaces/TagSurface.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "DDraw/Surfaces/Surface.h" - -namespace DDraw -{ - class TagSurface : public Surface - { - public: - virtual ~TagSurface(); - - static HRESULT create(CompatRef dd); - static TagSurface* get(void* ddObject); - }; -} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index cbbafc9..2f19f50 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -188,7 +188,6 @@ - @@ -261,7 +260,6 @@ - diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 27d1d81..cd99b96 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -255,9 +255,6 @@ Header Files\DDraw\Surfaces - - Header Files\DDraw\Surfaces - Header Files\Direct3d\Visitors @@ -449,9 +446,6 @@ Source Files\DDraw\Surfaces - - Source Files\DDraw\Surfaces - Source Files\Direct3d diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index 6a40bb0..3c03b16 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -84,7 +84,7 @@ namespace #define LOAD_ORIGINAL_PROC(procName) \ Dll::g_origProcs.procName = Compat::getProcAddress(g_origDDrawModule, #procName); -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { @@ -96,7 +96,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) char currentDllPath[MAX_PATH] = {}; GetModuleFileName(hinstDLL, currentDllPath, MAX_PATH); - Compat::Log() << "Loading DDrawCompat from " << currentDllPath; + Compat::Log() << "Loading DDrawCompat " << (lpvReserved ? "statically" : "dynamically") + << " from " << currentDllPath; char systemDirectory[MAX_PATH] = {}; GetSystemDirectory(systemDirectory, MAX_PATH); @@ -138,17 +139,24 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) } else if (fdwReason == DLL_PROCESS_DETACH) { - Compat::Log() << "Detaching DDrawCompat"; - DDraw::uninstallHooks(); - D3dDdi::uninstallHooks(); - Gdi::uninstallHooks(); - Compat::unhookAllFunctions(); - FreeLibrary(g_origDInputModule); - FreeLibrary(g_origDDrawModule); + Compat::Log() << "Detaching DDrawCompat due to " << (lpvReserved ? "process termination" : "FreeLibrary"); + if (!lpvReserved) + { + DDraw::uninstallHooks(); + D3dDdi::uninstallHooks(); + Gdi::uninstallHooks(); + Compat::unhookAllFunctions(); + FreeLibrary(g_origDInputModule); + FreeLibrary(g_origDDrawModule); + } Win32::FontSmoothing::setSystemSettingsForced(Win32::FontSmoothing::g_origSystemSettings); timeEndPeriod(1); Compat::Log() << "DDrawCompat detached successfully"; } + else if (fdwReason == DLL_THREAD_DETACH) + { + Gdi::dllThreadDetach(); + } return TRUE; } diff --git a/DDrawCompat/Gdi/Caret.cpp b/DDrawCompat/Gdi/Caret.cpp index e626c4f..e227292 100644 --- a/DDrawCompat/Gdi/Caret.cpp +++ b/DDrawCompat/Gdi/Caret.cpp @@ -25,7 +25,6 @@ namespace bool isDrawn; }; - CaretData g_caret = {}; long long g_qpcLastBlink = 0; diff --git a/DDrawCompat/Gdi/Dc.cpp b/DDrawCompat/Gdi/Dc.cpp index a6c63d3..f157531 100644 --- a/DDrawCompat/Gdi/Dc.cpp +++ b/DDrawCompat/Gdi/Dc.cpp @@ -18,14 +18,15 @@ namespace HDC dc; DWORD refCount; HDC origDc; + DWORD threadId; int savedState; }; - typedef std::unique_ptr OrigDc; typedef std::unordered_map CompatDcMap; CompatDcMap g_origDcToCompatDc; - thread_local std::vector g_threadDcs; + + void restoreDc(const CompatDc& compatDc); void copyDcAttributes(const CompatDc& compatDc, HDC origDc, const POINT& origin) { @@ -79,13 +80,12 @@ namespace MoveToEx(compatDc.dc, currentPos.x, currentPos.y, nullptr); } - void deleteDc(HDC origDc) + void restoreDc(const CompatDc& compatDc) { - DDraw::ScopedThreadLock lock; - auto it = g_origDcToCompatDc.find(origDc); - RestoreDC(it->second.dc, it->second.savedState); - Gdi::DcCache::deleteDc(it->second.dc); - g_origDcToCompatDc.erase(origDc); + // Bitmap may have changed during VirtualScreen::update, do not let RestoreDC restore the old one + HGDIOBJ bitmap = GetCurrentObject(compatDc.dc, OBJ_BITMAP); + RestoreDC(compatDc.dc, compatDc.savedState); + SelectObject(compatDc.dc, bitmap); } void setClippingRegion(HDC compatDc, HDC origDc, HWND hwnd, const POINT& origin) @@ -115,6 +115,37 @@ namespace Gdi { namespace Dc { + void dllProcessDetach() + { + DDraw::ScopedThreadLock lock; + for (auto& origDcToCompatDc : g_origDcToCompatDc) + { + restoreDc(origDcToCompatDc.second); + Gdi::DcCache::deleteDc(origDcToCompatDc.second.dc); + } + g_origDcToCompatDc.clear(); + } + + void dllThreadDetach() + { + DDraw::ScopedThreadLock lock; + const DWORD threadId = GetCurrentThreadId(); + auto it = g_origDcToCompatDc.begin(); + while (it != g_origDcToCompatDc.end()) + { + if (threadId == it->second.threadId) + { + restoreDc(it->second); + Gdi::DcCache::deleteDc(it->second.dc); + it = g_origDcToCompatDc.erase(it); + } + else + { + ++it; + } + } + } + HDC getDc(HDC origDc) { if (!origDc || OBJ_DC != GetObjectType(origDc) || DT_RASDISPLAY != GetDeviceCaps(origDc, TECHNOLOGY)) @@ -161,8 +192,8 @@ namespace Gdi compatDc.refCount = 1; compatDc.origDc = origDc; + compatDc.threadId = GetCurrentThreadId(); g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc)); - g_threadDcs.emplace_back(origDc, &deleteDc); return compatDc.dc; } @@ -188,12 +219,7 @@ namespace Gdi --compatDc.refCount; if (0 == compatDc.refCount) { - auto threadDcIter = std::find_if(g_threadDcs.begin(), g_threadDcs.end(), - [origDc](const OrigDc& dc) { return dc.get() == origDc; }); - threadDcIter->release(); - g_threadDcs.erase(threadDcIter); - - RestoreDC(compatDc.dc, compatDc.savedState); + restoreDc(compatDc); Gdi::DcCache::releaseDc(compatDc.dc); g_origDcToCompatDc.erase(origDc); } diff --git a/DDrawCompat/Gdi/Dc.h b/DDrawCompat/Gdi/Dc.h index a3a78e1..1dba9f0 100644 --- a/DDrawCompat/Gdi/Dc.h +++ b/DDrawCompat/Gdi/Dc.h @@ -8,6 +8,8 @@ namespace Gdi { namespace Dc { + void dllProcessDetach(); + void dllThreadDetach(); HDC getDc(HDC origDc); HDC getOrigDc(HDC dc); void releaseDc(HDC origDc); diff --git a/DDrawCompat/Gdi/DcCache.cpp b/DDrawCompat/Gdi/DcCache.cpp index 902642c..89571e3 100644 --- a/DDrawCompat/Gdi/DcCache.cpp +++ b/DDrawCompat/Gdi/DcCache.cpp @@ -1,14 +1,15 @@ +#include +#include #include #include +#include "DDraw/ScopedThreadLock.h" #include "Gdi/DcCache.h" #include "Gdi/VirtualScreen.h" namespace { - typedef std::unique_ptr CachedDc; - - thread_local std::vector g_cache; + std::map> g_threadIdToDcCache; } namespace Gdi @@ -17,29 +18,69 @@ namespace Gdi { void deleteDc(HDC cachedDc) { - Gdi::VirtualScreen::deleteDc(cachedDc); + DDraw::ScopedThreadLock lock; + for (auto& threadIdToDcCache : g_threadIdToDcCache) + { + auto& dcCache = threadIdToDcCache.second; + auto it = std::find(dcCache.begin(), dcCache.end(), cachedDc); + if (it != dcCache.end()) + { + Gdi::VirtualScreen::deleteDc(*it); + dcCache.erase(it); + return; + } + } + } + + void dllProcessDetach() + { + DDraw::ScopedThreadLock lock; + for (auto& threadIdToDcCache : g_threadIdToDcCache) + { + for (HDC dc : threadIdToDcCache.second) + { + Gdi::VirtualScreen::deleteDc(dc); + } + } + g_threadIdToDcCache.clear(); + } + + void dllThreadDetach() + { + DDraw::ScopedThreadLock lock; + auto it = g_threadIdToDcCache.find(GetCurrentThreadId()); + if (it == g_threadIdToDcCache.end()) + { + return; + } + + for (HDC dc : it->second) + { + Gdi::VirtualScreen::deleteDc(dc); + } + + g_threadIdToDcCache.erase(it); } HDC getDc() { - HDC cachedDc = nullptr; + DDraw::ScopedThreadLock lock; + std::vector& dcCache = g_threadIdToDcCache[GetCurrentThreadId()]; - if (g_cache.empty()) + if (dcCache.empty()) { - cachedDc = Gdi::VirtualScreen::createDc(); - } - else - { - cachedDc = g_cache.back().release(); - g_cache.pop_back(); + return Gdi::VirtualScreen::createDc(); } - return cachedDc; + HDC dc = dcCache.back(); + dcCache.pop_back(); + return dc; } void releaseDc(HDC cachedDc) { - g_cache.emplace_back(CachedDc(cachedDc, &deleteDc)); + DDraw::ScopedThreadLock lock; + g_threadIdToDcCache[GetCurrentThreadId()].push_back(cachedDc); } } } diff --git a/DDrawCompat/Gdi/DcCache.h b/DDrawCompat/Gdi/DcCache.h index f5f68fa..ac69c0e 100644 --- a/DDrawCompat/Gdi/DcCache.h +++ b/DDrawCompat/Gdi/DcCache.h @@ -9,6 +9,8 @@ namespace Gdi namespace DcCache { void deleteDc(HDC cachedDc); + void dllProcessDetach(); + void dllThreadDetach(); HDC getDc(); void releaseDc(HDC cachedDc); } diff --git a/DDrawCompat/Gdi/DcFunctions.cpp b/DDrawCompat/Gdi/DcFunctions.cpp index b67ea34..f5db3aa 100644 --- a/DDrawCompat/Gdi/DcFunctions.cpp +++ b/DDrawCompat/Gdi/DcFunctions.cpp @@ -13,6 +13,39 @@ namespace { + class CompatDc + { + public: + CompatDc(HDC dc) : m_origDc(dc), m_compatDc(Gdi::Dc::getDc(dc)) + { + } + + CompatDc(const CompatDc&) = delete; + + CompatDc(CompatDc&& other) : m_origDc(nullptr), m_compatDc(nullptr) + { + std::swap(m_origDc, other.m_origDc); + std::swap(m_compatDc, other.m_compatDc); + } + + ~CompatDc() + { + if (m_compatDc) + { + Gdi::Dc::releaseDc(m_origDc); + } + } + + operator HDC() const + { + return m_compatDc ? m_compatDc : m_origDc; + } + + private: + HDC m_origDc; + HDC m_compatDc; + }; + struct ExcludeRgnForOverlappingWindowArgs { HRGN rgn; @@ -56,25 +89,9 @@ namespace return t; } - HDC replaceDc(HDC dc) + CompatDc replaceDc(HDC dc) { - HDC compatDc = Gdi::Dc::getDc(dc); - return compatDc ? compatDc : dc; - } - - template - void releaseDc(T) {} - - void releaseDc(HDC dc) - { - Gdi::Dc::releaseDc(dc); - } - - template - void releaseDc(T t, Params... params) - { - releaseDc(params...); - releaseDc(t); + return CompatDc(dc); } template @@ -90,7 +107,6 @@ namespace const bool isReadOnlyAccess = !hasDisplayDcArg(getDestinationDc(params...)); Gdi::GdiAccessGuard accessGuard(isReadOnlyAccess ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE); result = Compat::getOrigFuncPtr()(replaceDc(params)...); - releaseDc(params...); } else { @@ -135,7 +151,6 @@ namespace { Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); result = CALL_ORIG_FUNC(ExtTextOutW)(replaceDc(hdc), x, y, options, lprect, lpString, c, lpDx); - releaseDc(hdc); } } else @@ -155,7 +170,10 @@ namespace return FALSE; } - if (!IsWindowVisible(hwnd) || + DWORD windowPid = 0; + GetWindowThreadProcessId(hwnd, &windowPid); + if (GetCurrentProcessId() != windowPid || + !IsWindowVisible(hwnd) || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & (WS_EX_LAYERED | WS_EX_TRANSPARENT))) { return TRUE; @@ -248,8 +266,7 @@ namespace } ExcludeRgnForOverlappingWindowArgs args = { hrgn, GetAncestor(hwnd, GA_ROOT) }; - EnumThreadWindows(Gdi::getGdiThreadId(), excludeRgnForOverlappingWindow, - reinterpret_cast(&args)); + EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast(&args)); return 1; } @@ -282,8 +299,7 @@ namespace Gdi { HRGN rgn = getWindowRegion(hwnd); ExcludeRgnForOverlappingWindowArgs args = { rgn, hwnd }; - EnumThreadWindows(Gdi::getGdiThreadId(), excludeRgnForOverlappingWindow, - reinterpret_cast(&args)); + EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast(&args)); return rgn; } diff --git a/DDrawCompat/Gdi/Gdi.cpp b/DDrawCompat/Gdi/Gdi.cpp index f63066b..670f17b 100644 --- a/DDrawCompat/Gdi/Gdi.cpp +++ b/DDrawCompat/Gdi/Gdi.cpp @@ -1,5 +1,7 @@ #include "DDraw/Surfaces/PrimarySurface.h" #include "Gdi/Caret.h" +#include "Gdi/Dc.h" +#include "Gdi/DcCache.h" #include "Gdi/DcFunctions.h" #include "Gdi/Gdi.h" #include "Gdi/PaintHandlers.h" @@ -9,21 +11,27 @@ namespace { - DWORD g_gdiThreadId = 0; HDC g_screenDc = nullptr; BOOL CALLBACK redrawWindowCallback(HWND hwnd, LPARAM lParam) { - Gdi::redrawWindow(hwnd, reinterpret_cast(lParam)); + DWORD windowPid = 0; + GetWindowThreadProcessId(hwnd, &windowPid); + if (GetCurrentProcessId() == windowPid) + { + Gdi::redrawWindow(hwnd, reinterpret_cast(lParam)); + } return TRUE; } } namespace Gdi { - DWORD getGdiThreadId() + void dllThreadDetach() { - return g_gdiThreadId; + WinProc::dllThreadDetach(); + Dc::dllThreadDetach(); + DcCache::dllThreadDetach(); } HDC getScreenDc() @@ -38,19 +46,18 @@ namespace Gdi void installHooks() { - g_gdiThreadId = GetCurrentThreadId(); g_screenDc = GetDC(nullptr); - Gdi::DcFunctions::installHooks(); - Gdi::PaintHandlers::installHooks(); - Gdi::ScrollFunctions::installHooks(); - Gdi::WinProc::installHooks(); - Gdi::Caret::installHooks(); + DcFunctions::installHooks(); + PaintHandlers::installHooks(); + ScrollFunctions::installHooks(); + WinProc::installHooks(); + Caret::installHooks(); } void redraw(HRGN rgn) { - EnumThreadWindows(g_gdiThreadId, &redrawWindowCallback, reinterpret_cast(rgn)); + EnumWindows(&redrawWindowCallback, reinterpret_cast(rgn)); } void redrawWindow(HWND hwnd, HRGN rgn) @@ -68,7 +75,6 @@ namespace Gdi } RedrawWindow(hwnd, nullptr, rgn, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); - RedrawWindow(hwnd, nullptr, rgn, RDW_ERASENOW); if (rgn) { @@ -85,9 +91,10 @@ namespace Gdi void uninstallHooks() { - Gdi::Caret::uninstallHooks(); - Gdi::WinProc::uninstallHooks(); - Gdi::PaintHandlers::uninstallHooks(); + Caret::uninstallHooks(); + WinProc::uninstallHooks(); + Dc::dllProcessDetach(); + DcCache::dllProcessDetach(); ReleaseDC(nullptr, g_screenDc); } diff --git a/DDrawCompat/Gdi/Gdi.h b/DDrawCompat/Gdi/Gdi.h index 1f7828b..a3b95ab 100644 --- a/DDrawCompat/Gdi/Gdi.h +++ b/DDrawCompat/Gdi/Gdi.h @@ -10,7 +10,7 @@ namespace Gdi typedef void(*WindowPosChangeNotifyFunc)(); - DWORD getGdiThreadId(); + void dllThreadDetach(); HDC getScreenDc(); HRGN getVisibleWindowRgn(HWND hwnd); void installHooks(); diff --git a/DDrawCompat/Gdi/PaintHandlers.cpp b/DDrawCompat/Gdi/PaintHandlers.cpp index da6b59b..879d2d0 100644 --- a/DDrawCompat/Gdi/PaintHandlers.cpp +++ b/DDrawCompat/Gdi/PaintHandlers.cpp @@ -41,7 +41,6 @@ namespace LRESULT onPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); - HHOOK g_cbtProcHook = nullptr; int g_menuWndProcIndex = 0; int g_scrollBarWndProcIndex = 0; @@ -71,52 +70,6 @@ namespace } } - LRESULT CALLBACK cbtProc(int nCode, WPARAM wParam, LPARAM lParam) - { - Compat::LogEnter("cbtProc", Compat::hex(nCode), Compat::hex(wParam), Compat::hex(lParam)); - LRESULT result = 0; - - if (nCode < 0) - { - result = CallNextHookEx(nullptr, nCode, wParam, lParam); - } - else if (HCBT_CREATEWND == nCode) - { - HWND hwnd = reinterpret_cast(wParam); - WNDPROC wndProcA = reinterpret_cast(GetWindowLongA(hwnd, GWL_WNDPROC)); - WNDPROC wndProcW = reinterpret_cast(GetWindowLongW(hwnd, GWL_WNDPROC)); - - int index = -1; - if (wndProcA == g_user32WndProcA[g_menuWndProcIndex].oldWndProc || - wndProcW == g_user32WndProcW[g_menuWndProcIndex].oldWndProc) - { - index = g_menuWndProcIndex; - } - else if (wndProcA == g_user32WndProcA[g_scrollBarWndProcIndex].oldWndProc || - wndProcW == g_user32WndProcW[g_scrollBarWndProcIndex].oldWndProc) - { - index = g_scrollBarWndProcIndex; - } - - if (-1 != index) - { - if (IsWindowUnicode(hwnd)) - { - CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC, - reinterpret_cast(g_user32WndProcW[index].newWndProc)); - } - else - { - CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, - reinterpret_cast(g_user32WndProcA[index].newWndProc)); - } - } - } - - Compat::LogLeave("cbtProc", Compat::hex(nCode), Compat::hex(wParam), Compat::hex(lParam)) << result; - return result; - } - LRESULT comboBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) { return defPaintProc(hwnd, msg, wParam, lParam, origWndProc); @@ -481,13 +434,38 @@ namespace Gdi HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW); HOOK_FUNCTION(user32, DefDlgProcA, defDlgProcA); HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW); - - g_cbtProcHook = SetWindowsHookEx(WH_CBT, cbtProc, nullptr, Gdi::getGdiThreadId()); } - void uninstallHooks() + void onCreateWindow(HWND hwnd) { - UnhookWindowsHookEx(g_cbtProcHook); + WNDPROC wndProcA = reinterpret_cast(GetWindowLongA(hwnd, GWL_WNDPROC)); + WNDPROC wndProcW = reinterpret_cast(GetWindowLongW(hwnd, GWL_WNDPROC)); + + int index = -1; + if (wndProcA == g_user32WndProcA[g_menuWndProcIndex].oldWndProc || + wndProcW == g_user32WndProcW[g_menuWndProcIndex].oldWndProc) + { + index = g_menuWndProcIndex; + } + else if (wndProcA == g_user32WndProcA[g_scrollBarWndProcIndex].oldWndProc || + wndProcW == g_user32WndProcW[g_scrollBarWndProcIndex].oldWndProc) + { + index = g_scrollBarWndProcIndex; + } + + if (-1 != index) + { + if (IsWindowUnicode(hwnd)) + { + CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC, + reinterpret_cast(g_user32WndProcW[index].newWndProc)); + } + else + { + CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, + reinterpret_cast(g_user32WndProcA[index].newWndProc)); + } + } } } } diff --git a/DDrawCompat/Gdi/PaintHandlers.h b/DDrawCompat/Gdi/PaintHandlers.h index 1f84798..cb14bf1 100644 --- a/DDrawCompat/Gdi/PaintHandlers.h +++ b/DDrawCompat/Gdi/PaintHandlers.h @@ -5,6 +5,6 @@ namespace Gdi namespace PaintHandlers { void installHooks(); - void uninstallHooks(); + void onCreateWindow(HWND hwnd); } } diff --git a/DDrawCompat/Gdi/VirtualScreen.cpp b/DDrawCompat/Gdi/VirtualScreen.cpp index 52c5fad..576c72e 100644 --- a/DDrawCompat/Gdi/VirtualScreen.cpp +++ b/DDrawCompat/Gdi/VirtualScreen.cpp @@ -1,8 +1,8 @@ #include -#include "Common/ScopedCriticalSection.h" #include "DDraw/DirectDraw.h" #include "DDraw/Repository.h" +#include "DDraw/ScopedThreadLock.h" #include "Gdi/Gdi.h" #include "Gdi/Region.h" #include "Gdi/VirtualScreen.h" @@ -10,7 +10,6 @@ namespace { - CRITICAL_SECTION g_cs = {}; Gdi::Region g_region; RECT g_bounds = {}; DWORD g_bpp = 0; @@ -71,7 +70,7 @@ namespace Gdi { HDC createDc() { - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; std::unique_ptr dib(createDib(), DeleteObject); if (!dib) { @@ -100,7 +99,7 @@ namespace Gdi HBITMAP createDib() { - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; if (!g_surfaceFileMapping) { return nullptr; @@ -110,7 +109,7 @@ namespace Gdi HBITMAP createOffScreenDib(DWORD width, DWORD height) { - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; return createDibSection(width, height, nullptr); } @@ -122,7 +121,7 @@ namespace Gdi return nullptr; } - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; DDSURFACEDESC2 desc = {}; desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE; @@ -148,7 +147,7 @@ namespace Gdi return; } - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; DeleteObject(SelectObject(dc, g_stockBitmap)); DeleteDC(dc); g_dcs.erase(dc); @@ -156,19 +155,18 @@ namespace Gdi RECT getBounds() { - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; return g_bounds; } const Region& getRegion() { - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; return g_region; } void init() { - InitializeCriticalSection(&g_cs); update(); updatePalette(); } @@ -176,7 +174,7 @@ namespace Gdi bool update() { Compat::LogEnter("VirtualScreen::update"); - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; static auto prevDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1; const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness(); @@ -224,7 +222,7 @@ namespace Gdi void updatePalette() { - Compat::ScopedCriticalSection lock(g_cs); + DDraw::ScopedThreadLock lock; if (8 != g_bpp) { return; diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index c440c67..5217c6f 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -1,7 +1,6 @@ #define WIN32_LEAN_AND_MEAN #include -#include #include #include @@ -11,15 +10,20 @@ #include "Common/ScopedCriticalSection.h" #include "Gdi/AccessGuard.h" #include "Gdi/Dc.h" +#include "Gdi/PaintHandlers.h" #include "Gdi/ScrollBar.h" #include "Gdi/ScrollFunctions.h" #include "Gdi/TitleBar.h" #include "Gdi/Window.h" #include "Gdi/WinProc.h" +extern "C" IMAGE_DOS_HEADER __ImageBase; + namespace { - HHOOK g_callWndRetProcHook = nullptr; + std::multimap g_threadIdToHook; + Compat::CriticalSection g_threadIdToHookCs; + HWINEVENTHOOK g_objectCreateEventHook = nullptr; HWINEVENTHOOK g_objectStateChangeEventHook = nullptr; std::set g_windowPosChangeNotifyFuncs; @@ -35,13 +39,9 @@ namespace auto ret = reinterpret_cast(lParam); Compat::LogEnter("callWndRetProc", Compat::hex(nCode), Compat::hex(wParam), ret); - if (HC_ACTION == nCode) + if (HC_ACTION == nCode && !Gdi::Window::isPresentationWindow(ret->hwnd)) { - if (WM_CREATE == ret->message) - { - onCreateWindow(ret->hwnd); - } - else if (WM_DESTROY == ret->message) + if (WM_DESTROY == ret->message) { onDestroyWindow(ret->hwnd); } @@ -79,8 +79,25 @@ namespace &disableTransitions, sizeof(disableTransitions)); } + void hookThread(DWORD threadId) + { + Compat::ScopedCriticalSection lock(g_threadIdToHookCs); + if (g_threadIdToHook.end() == g_threadIdToHook.find(threadId)) + { + g_threadIdToHook.emplace(threadId, + SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId)); + } + } + BOOL CALLBACK initTopLevelWindow(HWND hwnd, LPARAM /*lParam*/) { + DWORD windowPid = 0; + GetWindowThreadProcessId(hwnd, &windowPid); + if (GetCurrentProcessId() != windowPid) + { + return TRUE; + } + onCreateWindow(hwnd); if (!(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) { @@ -90,6 +107,21 @@ namespace return TRUE; } + void CALLBACK objectCreateEvent( + HWINEVENTHOOK /*hWinEventHook*/, + DWORD /*event*/, + HWND hwnd, + LONG idObject, + LONG /*idChild*/, + DWORD /*dwEventThread*/, + DWORD /*dwmsEventTime*/) + { + if (OBJID_WINDOW == idObject && !Gdi::Window::isPresentationWindow(hwnd)) + { + onCreateWindow(hwnd); + } + } + void CALLBACK objectStateChangeEvent( HWINEVENTHOOK /*hWinEventHook*/, DWORD /*event*/, @@ -149,9 +181,14 @@ namespace void onCreateWindow(HWND hwnd) { + hookThread(GetWindowThreadProcessId(hwnd, nullptr)); disableDwmAttributes(hwnd); removeDropShadow(hwnd); - Gdi::Window::add(hwnd); + Gdi::PaintHandlers::onCreateWindow(hwnd); + if (Gdi::Window::isTopLevelNonLayeredWindow(hwnd)) + { + Gdi::Window::add(hwnd); + } } void onDestroyWindow(HWND hwnd) @@ -191,14 +228,30 @@ namespace Gdi { namespace WinProc { + void dllThreadDetach() + { + Compat::ScopedCriticalSection lock(g_threadIdToHookCs); + const DWORD threadId = GetCurrentThreadId(); + for (auto threadIdToHook : g_threadIdToHook) + { + if (threadId == threadIdToHook.first) + { + UnhookWindowsHookEx(threadIdToHook.second); + } + } + g_threadIdToHook.erase(threadId); + } + void installHooks() { - const DWORD threadId = Gdi::getGdiThreadId(); - g_callWndRetProcHook = SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); + g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, + reinterpret_cast(&__ImageBase), &objectCreateEvent, + GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); g_objectStateChangeEventHook = SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE, - nullptr, &objectStateChangeEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); + reinterpret_cast(&__ImageBase), &objectStateChangeEvent, + GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); - EnumThreadWindows(threadId, initTopLevelWindow, 0); + EnumWindows(initTopLevelWindow, 0); } void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) @@ -209,7 +262,16 @@ namespace Gdi void uninstallHooks() { UnhookWinEvent(g_objectStateChangeEventHook); - UnhookWindowsHookEx(g_callWndRetProcHook); + UnhookWinEvent(g_objectCreateEventHook); + + { + Compat::ScopedCriticalSection lock(g_threadIdToHookCs); + for (auto threadIdToHook : g_threadIdToHook) + { + UnhookWindowsHookEx(threadIdToHook.second); + } + g_threadIdToHook.clear(); + } } } } diff --git a/DDrawCompat/Gdi/WinProc.h b/DDrawCompat/Gdi/WinProc.h index 928261e..33acfdf 100644 --- a/DDrawCompat/Gdi/WinProc.h +++ b/DDrawCompat/Gdi/WinProc.h @@ -6,6 +6,7 @@ namespace Gdi { namespace WinProc { + void dllThreadDetach(); void installHooks(); void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc); void uninstallHooks(); diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index 5258eba..aecff9e 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -66,13 +66,9 @@ namespace Gdi bool Window::add(HWND hwnd) { - const bool isTopLevelNonLayeredWindow = !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && - (!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow() || - getComboLBoxAtom() == GetClassLong(hwnd, GCW_ATOM)); - - if (isTopLevelNonLayeredWindow && !get(hwnd) && - GetClassLong(hwnd, GCW_ATOM) != getPresentationWindowClassAtom()) + if (isTopLevelNonLayeredWindow(hwnd) && !get(hwnd)) { + DDraw::ScopedThreadLock lock; s_windows.emplace(hwnd, std::make_shared(hwnd)); return true; } @@ -143,7 +139,14 @@ namespace Gdi bool Window::isPresentationWindow(HWND hwnd) { - return GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom(); + return IsWindow(hwnd) && GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom(); + } + + bool Window::isTopLevelNonLayeredWindow(HWND hwnd) + { + return !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && + (!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow() || + getComboLBoxAtom() == GetClassLong(hwnd, GCW_ATOM)); } void Window::remove(HWND hwnd) @@ -175,14 +178,14 @@ namespace Gdi newVisibleRegion &= VirtualScreen::getRegion(); } - if (m_presentationWindow) + if (m_presentationWindow && GetCurrentThreadId() == GetWindowThreadProcessId(m_hwnd, nullptr)) { SetWindowPos(m_presentationWindow, nullptr, newWindowRect.left, newWindowRect.top, newWindowRect.right - newWindowRect.left, newWindowRect.bottom - newWindowRect.top, SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW); } } - else if (m_presentationWindow) + else if (m_presentationWindow && GetCurrentThreadId() == GetWindowThreadProcessId(m_hwnd, nullptr)) { ShowWindow(m_presentationWindow, SW_HIDE); } diff --git a/DDrawCompat/Gdi/Window.h b/DDrawCompat/Gdi/Window.h index 122c04a..593182c 100644 --- a/DDrawCompat/Gdi/Window.h +++ b/DDrawCompat/Gdi/Window.h @@ -29,6 +29,7 @@ namespace Gdi static std::map> getWindows(); static bool isPresentationWindow(HWND hwnd); + static bool isTopLevelNonLayeredWindow(HWND hwnd); static void updateAll(); private: diff --git a/DDrawCompat/Win32/DisplayMode.cpp b/DDrawCompat/Win32/DisplayMode.cpp index b610a1d..9ee934f 100644 --- a/DDrawCompat/Win32/DisplayMode.cpp +++ b/DDrawCompat/Win32/DisplayMode.cpp @@ -4,7 +4,7 @@ #include "Common/CompatPtr.h" #include "Common/Hook.h" #include "DDraw/DirectDraw.h" -#include "DDraw/RealPrimarySurface.h" +#include "DDraw/ScopedThreadLock.h" #include "Win32/DisplayMode.h" BOOL WINAPI DWM8And16Bit_IsShimApplied_CallOut() { return FALSE; }; @@ -48,12 +48,12 @@ namespace EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, CStr lpszDeviceName, DevMode* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam) { + DDraw::ScopedThreadLock lock; DevMode prevDevMode = {}; if (!(dwflags & CDS_TEST)) { prevDevMode.dmSize = sizeof(prevDevMode); origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &prevDevMode, 0); - DDraw::RealPrimarySurface::disableUpdates(); } BOOL result = FALSE; @@ -99,11 +99,6 @@ namespace } } - if (!(dwflags & CDS_TEST)) - { - DDraw::RealPrimarySurface::enableUpdates(); - } - return result; }