mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
Fixed display issues with user32 controls
This commit is contained in:
parent
537ef9c595
commit
2f00b74a56
@ -96,10 +96,13 @@ std::ostream& operator<<(std::ostream& os, HRGN__& rgn)
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, HWND__& hwnd)
|
||||
{
|
||||
char name[256] = {};
|
||||
GetClassName(&hwnd, name, sizeof(name));
|
||||
char name[256] = "INVALID";
|
||||
RECT rect = {};
|
||||
GetWindowRect(&hwnd, &rect);
|
||||
if (IsWindow(&hwnd))
|
||||
{
|
||||
GetClassName(&hwnd, name, sizeof(name));
|
||||
GetWindowRect(&hwnd, &rect);
|
||||
}
|
||||
return os << "WND(" << static_cast<void*>(&hwnd) << ',' << name << ',' << rect << ')';
|
||||
}
|
||||
|
||||
|
@ -22,18 +22,6 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
struct BltToWindowViaGdiArgs
|
||||
{
|
||||
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc;
|
||||
Gdi::Region* primaryRegion;
|
||||
|
||||
BltToWindowViaGdiArgs()
|
||||
: virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc)
|
||||
, primaryRegion(nullptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void onRelease();
|
||||
DWORD WINAPI updateThreadProc(LPVOID lpParameter);
|
||||
|
||||
@ -70,65 +58,61 @@ namespace
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CALLBACK bltToWindow(HWND hwnd, LPARAM lParam)
|
||||
void bltToWindow(CompatRef<IDirectDrawSurface7> src)
|
||||
{
|
||||
if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd))
|
||||
for (auto windowPair : Gdi::Window::getWindows())
|
||||
{
|
||||
return TRUE;
|
||||
if (IsWindowVisible(windowPair.first))
|
||||
{
|
||||
g_clipper->SetHWnd(g_clipper, 0, windowPair.second->getPresentationWindow());
|
||||
g_frontBuffer->Blt(g_frontBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
g_clipper->SetHWnd(g_clipper, 0, hwnd);
|
||||
auto src = reinterpret_cast<IDirectDrawSurface7*>(lParam);
|
||||
g_frontBuffer->Blt(g_frontBuffer, nullptr, src, nullptr, DDBLT_WAIT, nullptr);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CALLBACK bltToWindowViaGdi(HWND hwnd, LPARAM lParam)
|
||||
void bltToWindowViaGdi(Gdi::Region* primaryRegion)
|
||||
{
|
||||
if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
|
||||
|
||||
auto window = Gdi::Window::get(GetParent(hwnd));
|
||||
if (!window)
|
||||
for (auto windowPair : Gdi::Window::getWindows())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if (!IsWindowVisible(windowPair.first))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Gdi::Region visibleRegion = window->getVisibleRegion();
|
||||
if (visibleRegion.isEmpty())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto& args = *reinterpret_cast<BltToWindowViaGdiArgs*>(lParam);
|
||||
if (args.primaryRegion)
|
||||
{
|
||||
visibleRegion -= *args.primaryRegion;
|
||||
Gdi::Region visibleRegion = windowPair.second->getVisibleRegion();
|
||||
if (visibleRegion.isEmpty())
|
||||
{
|
||||
return TRUE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.virtualScreenDc)
|
||||
{
|
||||
args.virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
|
||||
if (!args.virtualScreenDc)
|
||||
if (primaryRegion)
|
||||
{
|
||||
return FALSE;
|
||||
visibleRegion -= *primaryRegion;
|
||||
if (visibleRegion.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!virtualScreenDc)
|
||||
{
|
||||
virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
|
||||
if (!virtualScreenDc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Gdi::GdiAccessGuard gdiAccessGuard(Gdi::ACCESS_READ);
|
||||
HWND presentationWindow = windowPair.second->getPresentationWindow();
|
||||
HDC dc = GetWindowDC(presentationWindow);
|
||||
RECT rect = windowPair.second->getWindowRect();
|
||||
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
|
||||
virtualScreenDc.get(), rect.left, rect.top, SRCCOPY);
|
||||
ReleaseDC(presentationWindow, dc);
|
||||
}
|
||||
|
||||
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_READ);
|
||||
HDC presentationWindowDc = GetWindowDC(hwnd);
|
||||
RECT rect = window->getWindowRect();
|
||||
CALL_ORIG_FUNC(BitBlt)(presentationWindowDc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
|
||||
args.virtualScreenDc.get(), rect.left, rect.top, SRCCOPY);
|
||||
ReleaseDC(hwnd, presentationWindowDc);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void bltVisibleLayeredWindowsToBackBuffer()
|
||||
@ -174,7 +158,7 @@ namespace
|
||||
{
|
||||
if (!g_isFullScreen)
|
||||
{
|
||||
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast<LPARAM>(&src));
|
||||
bltToWindow(src);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -364,19 +348,15 @@ namespace
|
||||
|
||||
Gdi::VirtualScreen::update();
|
||||
|
||||
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
|
||||
if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost())
|
||||
{
|
||||
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
|
||||
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
|
||||
bltToWindowViaGdi(nullptr);
|
||||
Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()) << false;
|
||||
return;
|
||||
}
|
||||
|
||||
Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect());
|
||||
bltToWindowViaGdiArgs.primaryRegion = &primaryRegion;
|
||||
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
|
||||
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
|
||||
bltToWindowViaGdi(&primaryRegion);
|
||||
|
||||
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(src.get()));
|
||||
if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8)
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include "Common/Hook.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/ScopedCriticalSection.h"
|
||||
#include "DDraw/ScopedThreadLock.h"
|
||||
#include "Gdi/Dc.h"
|
||||
#include "Gdi/DcCache.h"
|
||||
#include "Gdi/Gdi.h"
|
||||
@ -24,7 +24,6 @@ namespace
|
||||
typedef std::unique_ptr<HDC__, void(*)(HDC)> OrigDc;
|
||||
typedef std::unordered_map<HDC, CompatDc> CompatDcMap;
|
||||
|
||||
CRITICAL_SECTION g_cs;
|
||||
CompatDcMap g_origDcToCompatDc;
|
||||
thread_local std::vector<OrigDc> g_threadDcs;
|
||||
|
||||
@ -82,7 +81,7 @@ namespace
|
||||
|
||||
void deleteDc(HDC origDc)
|
||||
{
|
||||
Compat::ScopedCriticalSection lock(g_cs);
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto it = g_origDcToCompatDc.find(origDc);
|
||||
RestoreDC(it->second.dc, it->second.savedState);
|
||||
Gdi::DcCache::deleteDc(it->second.dc);
|
||||
@ -110,24 +109,6 @@ namespace
|
||||
}
|
||||
DeleteObject(clipRgn);
|
||||
}
|
||||
|
||||
void updateWindow(HWND wnd)
|
||||
{
|
||||
auto window = Gdi::Window::get(wnd);
|
||||
if (!window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RECT windowRect = {};
|
||||
GetWindowRect(wnd, &windowRect);
|
||||
|
||||
RECT cachedWindowRect = window->getWindowRect();
|
||||
if (!EqualRect(&windowRect, &cachedWindowRect))
|
||||
{
|
||||
Gdi::Window::updateAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Gdi
|
||||
@ -141,7 +122,7 @@ namespace Gdi
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compat::ScopedCriticalSection lock(g_cs);
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto it = g_origDcToCompatDc.find(origDc);
|
||||
if (it != g_origDcToCompatDc.end())
|
||||
{
|
||||
@ -150,10 +131,15 @@ namespace Gdi
|
||||
}
|
||||
|
||||
const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc);
|
||||
const HWND rootWnd = wnd ? GetAncestor(wnd, GA_ROOT) : nullptr;
|
||||
auto rootWnd = wnd ? GetAncestor(wnd, GA_ROOT) : nullptr;
|
||||
if (rootWnd && GetDesktopWindow() != rootWnd)
|
||||
{
|
||||
updateWindow(rootWnd);
|
||||
auto rootWindow(Window::get(rootWnd));
|
||||
if (!rootWindow)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
rootWindow->updateWindow();
|
||||
}
|
||||
|
||||
CompatDc compatDc;
|
||||
@ -183,20 +169,15 @@ namespace Gdi
|
||||
|
||||
HDC getOrigDc(HDC dc)
|
||||
{
|
||||
Compat::ScopedCriticalSection lock(g_cs);
|
||||
DDraw::ScopedThreadLock lock;
|
||||
const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(),
|
||||
[dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; });
|
||||
return it != g_origDcToCompatDc.end() ? it->first : dc;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
InitializeCriticalSection(&g_cs);
|
||||
}
|
||||
|
||||
void releaseDc(HDC origDc)
|
||||
{
|
||||
Compat::ScopedCriticalSection lock(g_cs);
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto it = g_origDcToCompatDc.find(origDc);
|
||||
if (it == g_origDcToCompatDc.end())
|
||||
{
|
||||
|
@ -10,7 +10,6 @@ namespace Gdi
|
||||
{
|
||||
HDC getDc(HDC origDc);
|
||||
HDC getOrigDc(HDC dc);
|
||||
void init();
|
||||
void releaseDc(HDC origDc);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,9 @@
|
||||
#include "Gdi/Dc.h"
|
||||
#include "Gdi/DcFunctions.h"
|
||||
#include "Gdi/Gdi.h"
|
||||
#include "Gdi/Region.h"
|
||||
#include "Gdi/VirtualScreen.h"
|
||||
#include "Gdi/Window.h"
|
||||
#include "Win32/DisplayMode.h"
|
||||
|
||||
namespace
|
||||
@ -20,7 +22,7 @@ namespace
|
||||
std::unordered_map<void*, const char*> g_funcNames;
|
||||
|
||||
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename... Params>
|
||||
DWORD getDdLockFlags(Params... params);
|
||||
HDC getDestinationDc(Params... params);
|
||||
|
||||
HRGN getWindowRegion(HWND hwnd);
|
||||
|
||||
@ -85,7 +87,7 @@ namespace
|
||||
Result result = 0;
|
||||
if (hasDisplayDcArg(params...))
|
||||
{
|
||||
const bool isReadOnlyAccess = getDdLockFlags<OrigFuncPtr, origFunc>(params...) & DDLOCK_READONLY;
|
||||
const bool isReadOnlyAccess = !hasDisplayDcArg(getDestinationDc<OrigFuncPtr, origFunc>(params...));
|
||||
Gdi::GdiAccessGuard accessGuard(isReadOnlyAccess ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE);
|
||||
result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(replaceDc(params)...);
|
||||
releaseDc(params...);
|
||||
@ -102,6 +104,49 @@ namespace
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
BOOL WINAPI compatGdiDcFunc<decltype(&ExtTextOutW), &ExtTextOutW>(
|
||||
HDC hdc, int x, int y, UINT options, const RECT* lprect, LPCWSTR lpString, UINT c, const INT* lpDx)
|
||||
{
|
||||
Compat::LogEnter("ExtTextOutW", hdc, x, y, options, lprect, lpString, c, lpDx);
|
||||
|
||||
BOOL result = TRUE;
|
||||
if (hasDisplayDcArg(hdc))
|
||||
{
|
||||
HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(hdc);
|
||||
ATOM atom = static_cast<ATOM>(GetClassLong(hwnd, GCW_ATOM));
|
||||
POINT p = { x, y };
|
||||
if (Gdi::MENU_ATOM == atom)
|
||||
{
|
||||
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE);
|
||||
}
|
||||
else if (GetCurrentThreadId() == GetWindowThreadProcessId(hwnd, nullptr) &&
|
||||
LPtoDP(hdc, &p, 1) &&
|
||||
HTMENU == SendMessage(hwnd, WM_NCHITTEST, 0, (p.y << 16) | (p.x & 0xFFFF)))
|
||||
{
|
||||
WINDOWINFO wi = {};
|
||||
GetWindowInfo(hwnd, &wi);
|
||||
Gdi::Region ncRegion(wi.rcWindow);
|
||||
ncRegion -= wi.rcClient;
|
||||
ncRegion.offset(-wi.rcClient.left, -wi.rcClient.top);
|
||||
RedrawWindow(hwnd, nullptr, ncRegion, RDW_INVALIDATE | RDW_FRAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
|
||||
result = CALL_ORIG_FUNC(ExtTextOutW)(replaceDc(hdc), x, y, options, lprect, lpString, c, lpDx);
|
||||
releaseDc(hdc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = CALL_ORIG_FUNC(ExtTextOutW)(hdc, x, y, options, lprect, lpString, c, lpDx);
|
||||
}
|
||||
|
||||
Compat::LogLeave("ExtTextOutW", hdc, x, y, options, lprect, lpString, c, lpDx) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
auto& args = *reinterpret_cast<ExcludeRgnForOverlappingWindowArgs*>(lParam);
|
||||
@ -129,84 +174,40 @@ namespace
|
||||
return &compatGdiDcFunc<OrigFuncPtr, origFunc, Result, Params...>;
|
||||
}
|
||||
|
||||
DWORD getDdLockFlagsBlt(HDC hdcDest, HDC hdcSrc)
|
||||
HDC getFirstDc()
|
||||
{
|
||||
return hasDisplayDcArg(hdcSrc) && !hasDisplayDcArg(hdcDest) ? DDLOCK_READONLY : 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename... Params>
|
||||
HDC getFirstDc(HDC dc, Params...)
|
||||
{
|
||||
return dc;
|
||||
}
|
||||
|
||||
template <typename FirstParam, typename... Params>
|
||||
HDC getFirstDc(FirstParam, Params... params)
|
||||
{
|
||||
return getFirstDc(params...);
|
||||
}
|
||||
|
||||
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename... Params>
|
||||
DWORD getDdLockFlags(Params...)
|
||||
HDC getDestinationDc(Params... params)
|
||||
{
|
||||
return 0;
|
||||
return getFirstDc(params...);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&AlphaBlend), &AlphaBlend>(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, BLENDFUNCTION)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&BitBlt), &BitBlt>(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, DWORD)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&GdiAlphaBlend), &GdiAlphaBlend>(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, BLENDFUNCTION)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&GdiTransparentBlt), &GdiTransparentBlt >(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, UINT)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&GetDIBits), &GetDIBits>(
|
||||
HDC getDestinationDc<decltype(&GetDIBits), &GetDIBits>(
|
||||
HDC, HBITMAP, UINT, UINT, LPVOID, LPBITMAPINFO, UINT)
|
||||
{
|
||||
return DDLOCK_READONLY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&GetPixel), &GetPixel>(HDC, int, int)
|
||||
HDC getDestinationDc<decltype(&GetPixel), &GetPixel>(HDC, int, int)
|
||||
{
|
||||
return DDLOCK_READONLY;
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&MaskBlt), &MaskBlt>(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, HBITMAP, int, int, DWORD)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&PlgBlt), &PlgBlt>(
|
||||
HDC hdcDest, const POINT*, HDC hdcSrc, int, int, int, int, HBITMAP, int, int)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&StretchBlt), &StretchBlt>(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, DWORD)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
}
|
||||
|
||||
template <>
|
||||
DWORD getDdLockFlags<decltype(&TransparentBlt), &TransparentBlt>(
|
||||
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, UINT)
|
||||
{
|
||||
return getDdLockFlagsBlt(hdcDest, hdcSrc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HRGN getWindowRegion(HWND hwnd)
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "DDraw/Surfaces/PrimarySurface.h"
|
||||
#include "Gdi/Caret.h"
|
||||
#include "Gdi/Dc.h"
|
||||
#include "Gdi/DcFunctions.h"
|
||||
#include "Gdi/Gdi.h"
|
||||
#include "Gdi/PaintHandlers.h"
|
||||
@ -37,20 +36,11 @@ namespace Gdi
|
||||
return DcFunctions::getVisibleWindowRgn(hwnd);
|
||||
}
|
||||
|
||||
void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc)
|
||||
{
|
||||
HWND hwnd = CreateWindow(className, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, 0);
|
||||
oldWndProc = reinterpret_cast<WNDPROC>(
|
||||
SetClassLongPtr(hwnd, GCLP_WNDPROC, reinterpret_cast<LONG>(newWndProc)));
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
|
||||
void installHooks()
|
||||
{
|
||||
g_gdiThreadId = GetCurrentThreadId();
|
||||
g_screenDc = GetDC(nullptr);
|
||||
|
||||
Gdi::Dc::init();
|
||||
Gdi::DcFunctions::installHooks();
|
||||
Gdi::PaintHandlers::installHooks();
|
||||
Gdi::ScrollFunctions::installHooks();
|
||||
|
@ -6,12 +6,13 @@
|
||||
|
||||
namespace Gdi
|
||||
{
|
||||
const ATOM MENU_ATOM = 0x8000;
|
||||
|
||||
typedef void(*WindowPosChangeNotifyFunc)();
|
||||
|
||||
DWORD getGdiThreadId();
|
||||
HDC getScreenDc();
|
||||
HRGN getVisibleWindowRgn(HWND hwnd);
|
||||
void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc);
|
||||
void installHooks();
|
||||
void redraw(HRGN rgn);
|
||||
void redrawWindow(HWND hwnd, HRGN rgn);
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Hook.h"
|
||||
#include "Common/Log.h"
|
||||
#include "DDraw/RealPrimarySurface.h"
|
||||
@ -8,56 +10,130 @@
|
||||
#include "Gdi/ScrollBar.h"
|
||||
#include "Gdi/ScrollFunctions.h"
|
||||
#include "Gdi/TitleBar.h"
|
||||
#include "Gdi/VirtualScreen.h"
|
||||
#include "Gdi/Window.h"
|
||||
#include "Win32/Registry.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
LRESULT WINAPI defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
|
||||
WNDPROC origWndProc, const char* origWndProcName);
|
||||
typedef LRESULT(*WndProcHook)(HWND, UINT, WPARAM, LPARAM, WNDPROC);
|
||||
|
||||
struct User32WndProc
|
||||
{
|
||||
WNDPROC oldWndProcTrampoline;
|
||||
WNDPROC oldWndProc;
|
||||
WNDPROC newWndProc;
|
||||
std::string name;
|
||||
|
||||
User32WndProc()
|
||||
: 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,
|
||||
const char* origWndProcName);
|
||||
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc);
|
||||
LRESULT onMenuPaint(HWND hwnd, WNDPROC origWndProc);
|
||||
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc);
|
||||
LRESULT onPaint(HWND hwnd, WNDPROC origWndProc);
|
||||
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
|
||||
|
||||
WNDPROC g_origButtonWndProc = nullptr;
|
||||
WNDPROC g_origComboListBoxWndProc = nullptr;
|
||||
WNDPROC g_origEditWndProc = nullptr;
|
||||
WNDPROC g_origListBoxWndProc = nullptr;
|
||||
WNDPROC g_origMenuWndProc = nullptr;
|
||||
WNDPROC g_origScrollBarWndProc = nullptr;
|
||||
HHOOK g_cbtProcHook = nullptr;
|
||||
int g_menuWndProcIndex = 0;
|
||||
int g_scrollBarWndProcIndex = 0;
|
||||
|
||||
LRESULT WINAPI buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
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)
|
||||
{
|
||||
Compat::LogEnter("buttonWndProc", hwnd, msg, wParam, lParam);
|
||||
LRESULT result = 0;
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_PAINT:
|
||||
result = onPaint(hwnd, g_origButtonWndProc);
|
||||
break;
|
||||
return onPaint(hwnd, origWndProc);
|
||||
|
||||
case WM_ENABLE:
|
||||
case WM_SETTEXT:
|
||||
case BM_SETCHECK:
|
||||
case BM_SETSTATE:
|
||||
result = CallWindowProc(g_origButtonWndProc, hwnd, msg, wParam, lParam);
|
||||
{
|
||||
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
|
||||
break;
|
||||
|
||||
default:
|
||||
result = CallWindowProc(g_origButtonWndProc, hwnd, msg, wParam, lParam);
|
||||
break;
|
||||
return result;
|
||||
}
|
||||
|
||||
Compat::LogLeave("buttonWndProc", hwnd, msg, wParam, lParam) << result;
|
||||
default:
|
||||
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
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<HWND>(wParam);
|
||||
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)
|
||||
{
|
||||
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<LONG>(g_user32WndProcW[index].newWndProc));
|
||||
}
|
||||
else
|
||||
{
|
||||
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC,
|
||||
reinterpret_cast<LONG>(g_user32WndProcA[index].newWndProc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Compat::LogLeave("cbtProc", Compat::hex(nCode), Compat::hex(wParam), Compat::hex(lParam)) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
LRESULT WINAPI comboListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
LRESULT comboBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, g_origComboListBoxWndProc, "comboListBoxWndProc");
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
}
|
||||
|
||||
LRESULT comboListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_NCPAINT:
|
||||
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LRESULT WINAPI defDlgProcA(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
@ -70,38 +146,31 @@ namespace
|
||||
return defPaintProc(hdlg, msg, wParam, lParam, CALL_ORIG_FUNC(DefDlgProcW), "defDlgProcW");
|
||||
}
|
||||
|
||||
LRESULT WINAPI defPaintProc(
|
||||
HWND hwnd,
|
||||
UINT msg,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam,
|
||||
WNDPROC origWndProc,
|
||||
const char* origWndProcName)
|
||||
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
Compat::LogEnter(origWndProcName, hwnd, msg, wParam, lParam);
|
||||
LRESULT result = 0;
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_ERASEBKGND:
|
||||
result = onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc);
|
||||
break;
|
||||
return onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc);
|
||||
|
||||
case WM_NCPAINT:
|
||||
result = onNcPaint(hwnd, wParam, origWndProc);
|
||||
break;
|
||||
return onNcPaint(hwnd, wParam, origWndProc);
|
||||
|
||||
case WM_PRINT:
|
||||
case WM_PRINTCLIENT:
|
||||
result = onPrint(hwnd, msg, reinterpret_cast<HDC>(wParam), lParam, origWndProc);
|
||||
break;
|
||||
return onPrint(hwnd, msg, reinterpret_cast<HDC>(wParam), lParam, origWndProc);
|
||||
|
||||
default:
|
||||
result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
break;
|
||||
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
Compat::LogLeave(origWndProcName, hwnd, msg, wParam, lParam) << result;
|
||||
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc,
|
||||
const char* origWndProcName)
|
||||
{
|
||||
Compat::LogEnter(origWndProcName, hwnd, Compat::hex(msg), Compat::hex(wParam), Compat::hex(lParam));
|
||||
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
Compat::LogLeave(origWndProcName, hwnd, Compat::hex(msg), Compat::hex(wParam), Compat::hex(lParam)) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -141,9 +210,9 @@ namespace
|
||||
0);
|
||||
}
|
||||
|
||||
LRESULT WINAPI editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
LRESULT editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, g_origEditWndProc, "editWndProc");
|
||||
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
if (0 == result && (WM_HSCROLL == msg || WM_VSCROLL == msg))
|
||||
{
|
||||
Gdi::ScrollFunctions::updateScrolledWindow(hwnd);
|
||||
@ -151,37 +220,88 @@ namespace
|
||||
return result;
|
||||
}
|
||||
|
||||
LRESULT WINAPI listBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
void hookUser32WndProc(const std::string& wndProcName, HWND hwnd, WNDPROC newWndProc,
|
||||
decltype(GetWindowLongPtr)* getWindowLong, std::vector<User32WndProc>& user32WndProc)
|
||||
{
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, g_origListBoxWndProc, "listBoxWndProc");
|
||||
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);
|
||||
|
||||
if (reinterpret_cast<DWORD>(wndProc.oldWndProcTrampoline) < 0xFFFF0000)
|
||||
{
|
||||
Compat::hookFunction(
|
||||
reinterpret_cast<void*&>(user32WndProc.back().oldWndProcTrampoline), newWndProc);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT WINAPI menuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
void hookUser32WndProcA(const char* className, WNDPROC newWndProc, const std::string& wndProcName)
|
||||
{
|
||||
Compat::LogEnter("menuWndProc", hwnd, msg, wParam, lParam);
|
||||
LRESULT result = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
void hookUser32WndProcW(const char* name, WNDPROC newWndProc, const std::string& wndProcName)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
LRESULT listBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
}
|
||||
|
||||
LRESULT mdiClientWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
}
|
||||
|
||||
LRESULT menuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_NCPAINT:
|
||||
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
return onNcPaint(hwnd, wParam, origWndProc);
|
||||
|
||||
case WM_PAINT:
|
||||
result = onMenuPaint(hwnd, g_origMenuWndProc);
|
||||
break;
|
||||
return onPaint(hwnd, origWndProc);
|
||||
|
||||
case WM_PRINTCLIENT:
|
||||
{
|
||||
RECT r = {};
|
||||
GetClientRect(hwnd, &r);
|
||||
HDC dc = CreateCompatibleDC(nullptr);
|
||||
HBITMAP dib = Gdi::VirtualScreen::createOffScreenDib(r.right, r.bottom);
|
||||
HGDIOBJ origBitmap = SelectObject(dc, dib);
|
||||
CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
|
||||
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast<WPARAM>(dc), lParam);
|
||||
CALL_ORIG_FUNC(BitBlt)(reinterpret_cast<HDC>(wParam), 0, 0, r.right, r.bottom, dc, 0, 0, SRCCOPY);
|
||||
SelectObject(dc, origBitmap);
|
||||
DeleteObject(dib);
|
||||
DeleteDC(dc);
|
||||
return result;
|
||||
}
|
||||
|
||||
case 0x1e5:
|
||||
if (-1 == wParam)
|
||||
{
|
||||
// Clearing of selection is not caught by WM_MENUSELECT when mouse leaves menu window
|
||||
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE);
|
||||
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE);
|
||||
}
|
||||
// fall through to default
|
||||
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
|
||||
default:
|
||||
result = CallWindowProc(g_origMenuWndProc, hwnd, msg, wParam, lParam);
|
||||
break;
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
}
|
||||
|
||||
Compat::LogLeave("menuWndProc", hwnd, msg, wParam, lParam) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc)
|
||||
@ -202,32 +322,6 @@ namespace
|
||||
return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
|
||||
}
|
||||
|
||||
LRESULT onMenuPaint(HWND hwnd, WNDPROC origWndProc)
|
||||
{
|
||||
if (!hwnd)
|
||||
{
|
||||
return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
|
||||
}
|
||||
|
||||
HDC dc = GetWindowDC(hwnd);
|
||||
HDC compatDc = Gdi::Dc::getDc(dc);
|
||||
if (compatDc)
|
||||
{
|
||||
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
|
||||
CallWindowProc(origWndProc, hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc),
|
||||
PRF_NONCLIENT | PRF_ERASEBKGND | PRF_CLIENT);
|
||||
ValidateRect(hwnd, nullptr);
|
||||
Gdi::Dc::releaseDc(dc);
|
||||
}
|
||||
else
|
||||
{
|
||||
CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
|
||||
}
|
||||
|
||||
ReleaseDC(hwnd, dc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc)
|
||||
{
|
||||
if (!hwnd)
|
||||
@ -249,7 +343,7 @@ namespace
|
||||
scrollBar.drawAll();
|
||||
scrollBar.excludeFromClipRegion();
|
||||
|
||||
SendMessage(hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc), PRF_NONCLIENT);
|
||||
CallWindowProc(origWndProc, hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc), PRF_NONCLIENT);
|
||||
|
||||
Gdi::Dc::releaseDc(windowDc);
|
||||
}
|
||||
@ -265,6 +359,7 @@ namespace
|
||||
return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
|
||||
}
|
||||
|
||||
DDraw::ScopedThreadLock lock;
|
||||
PAINTSTRUCT paint = {};
|
||||
HDC dc = BeginPaint(hwnd, &paint);
|
||||
HDC compatDc = Gdi::Dc::getDc(dc);
|
||||
@ -303,33 +398,52 @@ namespace
|
||||
return result;
|
||||
}
|
||||
|
||||
LRESULT WINAPI scrollBarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
LRESULT scrollBarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
Compat::LogEnter("scrollBarWndProc", hwnd, msg, wParam, lParam);
|
||||
LRESULT result = 0;
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_PAINT:
|
||||
result = onPaint(hwnd, g_origScrollBarWndProc);
|
||||
break;
|
||||
return onPaint(hwnd, origWndProc);
|
||||
|
||||
case WM_SETCURSOR:
|
||||
if (GetWindowLong(hwnd, GWL_STYLE) & (SBS_SIZEBOX | SBS_SIZEGRIP))
|
||||
{
|
||||
SetCursor(LoadCursor(nullptr, IDC_SIZENWSE));
|
||||
}
|
||||
result = TRUE;
|
||||
break;
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
result = CallWindowProc(g_origScrollBarWndProc, hwnd, msg, wParam, lParam);
|
||||
break;
|
||||
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
Compat::LogLeave("scrollBarWndProc", hwnd, msg, wParam, lParam) << result;
|
||||
LRESULT staticWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
|
||||
{
|
||||
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK user32WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
|
||||
const User32WndProc& user32WndProc, WndProcHook wndProcHook)
|
||||
{
|
||||
Compat::LogEnter(user32WndProc.name.c_str(),
|
||||
hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam));
|
||||
LRESULT result = wndProcHook(hwnd, uMsg, wParam, lParam, user32WndProc.oldWndProcTrampoline);
|
||||
Compat::LogLeave(user32WndProc.name.c_str(),
|
||||
hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam)) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
LRESULT CALLBACK user32WndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return user32WndProc(hwnd, uMsg, wParam, lParam, g_user32WndProcA[index], g_user32WndProcHook[index]);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
LRESULT CALLBACK user32WndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return user32WndProc(hwnd, uMsg, wParam, lParam, g_user32WndProcW[index], g_user32WndProcHook[index]);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Gdi
|
||||
@ -340,27 +454,40 @@ namespace Gdi
|
||||
{
|
||||
disableImmersiveContextMenus();
|
||||
|
||||
Gdi::hookWndProc("Button", g_origButtonWndProc, &buttonWndProc);
|
||||
Gdi::hookWndProc("ComboLBox", g_origComboListBoxWndProc, &comboListBoxWndProc);
|
||||
Gdi::hookWndProc("Edit", g_origEditWndProc, &editWndProc);
|
||||
Gdi::hookWndProc("ListBox", g_origListBoxWndProc, &listBoxWndProc);
|
||||
Gdi::hookWndProc("#32768", g_origMenuWndProc, &menuWndProc);
|
||||
Gdi::hookWndProc("ScrollBar", g_origScrollBarWndProc, &scrollBarWndProc);
|
||||
#define HOOK_USER32_WNDPROC(index, name, wndProcHook) \
|
||||
g_user32WndProcHook.push_back(wndProcHook); \
|
||||
hookUser32WndProcA(name, user32WndProcA<index>, #wndProcHook); \
|
||||
hookUser32WndProcW(name, user32WndProcW<index>, #wndProcHook)
|
||||
|
||||
g_user32WndProcA.reserve(9);
|
||||
g_user32WndProcW.reserve(9);
|
||||
|
||||
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);
|
||||
|
||||
g_scrollBarWndProcIndex = 5;
|
||||
g_menuWndProcIndex = 8;
|
||||
|
||||
#undef HOOK_USER32_WNDPROC
|
||||
|
||||
HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA);
|
||||
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()
|
||||
{
|
||||
Gdi::unhookWndProc("Button", g_origButtonWndProc);
|
||||
Gdi::unhookWndProc("ComboLBox", g_origComboListBoxWndProc);
|
||||
Gdi::unhookWndProc("Edit", g_origEditWndProc);
|
||||
Gdi::unhookWndProc("ListBox", g_origListBoxWndProc);
|
||||
Gdi::unhookWndProc("#32768", g_origMenuWndProc);
|
||||
Gdi::unhookWndProc("ScrollBar", g_origScrollBarWndProc);
|
||||
UnhookWindowsHookEx(g_cbtProcHook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#include "Common/Hook.h"
|
||||
#include "Common/Log.h"
|
||||
#include "DDraw/RealPrimarySurface.h"
|
||||
#include "DDraw/ScopedThreadLock.h"
|
||||
#include "Gdi/Gdi.h"
|
||||
#include "Gdi/ScrollFunctions.h"
|
||||
#include "Gdi/Window.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -55,10 +56,14 @@ namespace Gdi
|
||||
|
||||
void updateScrolledWindow(HWND hwnd)
|
||||
{
|
||||
DDraw::RealPrimarySurface::disableUpdates();
|
||||
RedrawWindow(hwnd, nullptr, nullptr,
|
||||
RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN | RDW_UPDATENOW);
|
||||
DDraw::RealPrimarySurface::enableUpdates();
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto window(Gdi::Window::get(hwnd));
|
||||
UINT flags = RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN | RDW_UPDATENOW;
|
||||
if (!window || window->getPresentationWindow() != hwnd)
|
||||
{
|
||||
flags |= RDW_FRAME;
|
||||
}
|
||||
RedrawWindow(hwnd, nullptr, nullptr, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,37 @@ namespace
|
||||
virtualScreenRegion |= monitorRegion;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HBITMAP createDibSection(DWORD width, DWORD height, HANDLE section)
|
||||
{
|
||||
struct BITMAPINFO256 : public BITMAPINFO
|
||||
{
|
||||
RGBQUAD bmiRemainingColors[255];
|
||||
};
|
||||
|
||||
BITMAPINFO256 bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
|
||||
bmi.bmiHeader.biWidth = width;
|
||||
bmi.bmiHeader.biHeight = -static_cast<LONG>(height);
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = static_cast<WORD>(g_bpp);
|
||||
bmi.bmiHeader.biCompression = 8 == g_bpp ? BI_RGB : BI_BITFIELDS;
|
||||
|
||||
if (8 == g_bpp)
|
||||
{
|
||||
memcpy(bmi.bmiColors, g_systemPalette, sizeof(g_systemPalette));
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto pf = DDraw::getRgbPixelFormat(g_bpp);
|
||||
reinterpret_cast<DWORD&>(bmi.bmiColors[0]) = pf.dwRBitMask;
|
||||
reinterpret_cast<DWORD&>(bmi.bmiColors[1]) = pf.dwGBitMask;
|
||||
reinterpret_cast<DWORD&>(bmi.bmiColors[2]) = pf.dwBBitMask;
|
||||
}
|
||||
|
||||
void* bits = nullptr;
|
||||
return CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, section, 0);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Gdi
|
||||
@ -74,34 +105,13 @@ namespace Gdi
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return createDibSection(g_width, g_height, g_surfaceFileMapping);
|
||||
}
|
||||
|
||||
struct BITMAPINFO256 : public BITMAPINFO
|
||||
{
|
||||
RGBQUAD bmiRemainingColors[255];
|
||||
};
|
||||
|
||||
BITMAPINFO256 bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
|
||||
bmi.bmiHeader.biWidth = g_width;
|
||||
bmi.bmiHeader.biHeight = -g_height;
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = static_cast<WORD>(g_bpp);
|
||||
bmi.bmiHeader.biCompression = 8 == g_bpp ? BI_RGB : BI_BITFIELDS;
|
||||
|
||||
if (8 == g_bpp)
|
||||
{
|
||||
memcpy(bmi.bmiColors, g_systemPalette, sizeof(g_systemPalette));
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto pf = DDraw::getRgbPixelFormat(g_bpp);
|
||||
reinterpret_cast<DWORD&>(bmi.bmiColors[0]) = pf.dwRBitMask;
|
||||
reinterpret_cast<DWORD&>(bmi.bmiColors[1]) = pf.dwGBitMask;
|
||||
reinterpret_cast<DWORD&>(bmi.bmiColors[2]) = pf.dwBBitMask;
|
||||
}
|
||||
|
||||
void* bits = nullptr;
|
||||
return CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, g_surfaceFileMapping, 0);
|
||||
HBITMAP createOffScreenDib(DWORD width, DWORD height)
|
||||
{
|
||||
Compat::ScopedCriticalSection lock(g_cs);
|
||||
return createDibSection(width, height, nullptr);
|
||||
}
|
||||
|
||||
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect)
|
||||
|
@ -14,6 +14,7 @@ namespace Gdi
|
||||
{
|
||||
HDC createDc();
|
||||
HBITMAP createDib();
|
||||
HBITMAP createOffScreenDib(DWORD width, DWORD height);
|
||||
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect);
|
||||
void deleteDc(HDC dc);
|
||||
|
||||
|
@ -27,14 +27,13 @@ namespace
|
||||
void onActivate(HWND hwnd);
|
||||
void onCreateWindow(HWND hwnd);
|
||||
void onDestroyWindow(HWND hwnd);
|
||||
void onMenuSelect();
|
||||
void onWindowPosChanged(HWND hwnd);
|
||||
void removeDropShadow(HWND hwnd);
|
||||
|
||||
LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
auto ret = reinterpret_cast<CWPRETSTRUCT*>(lParam);
|
||||
Compat::LogEnter("callWndRetProc", nCode, wParam, ret);
|
||||
Compat::LogEnter("callWndRetProc", Compat::hex(nCode), Compat::hex(wParam), ret);
|
||||
|
||||
if (HC_ACTION == nCode)
|
||||
{
|
||||
@ -62,14 +61,10 @@ namespace
|
||||
Gdi::ScrollFunctions::updateScrolledWindow(reinterpret_cast<HWND>(ret->lParam));
|
||||
}
|
||||
}
|
||||
else if (WM_MENUSELECT == ret->message)
|
||||
{
|
||||
onMenuSelect();
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT result = CallNextHookEx(nullptr, nCode, wParam, lParam);
|
||||
Compat::LogLeave("callWndRetProc", nCode, wParam, ret) << result;
|
||||
Compat::LogLeave("callWndRetProc", Compat::hex(nCode), Compat::hex(wParam), ret) << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -95,12 +90,6 @@ namespace
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool isTopLevelNonLayeredWindow(HWND hwnd)
|
||||
{
|
||||
return !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
|
||||
(!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow());
|
||||
}
|
||||
|
||||
void CALLBACK objectStateChangeEvent(
|
||||
HWINEVENTHOOK /*hWinEventHook*/,
|
||||
DWORD /*event*/,
|
||||
@ -160,12 +149,9 @@ namespace
|
||||
|
||||
void onCreateWindow(HWND hwnd)
|
||||
{
|
||||
if (isTopLevelNonLayeredWindow(hwnd))
|
||||
{
|
||||
disableDwmAttributes(hwnd);
|
||||
removeDropShadow(hwnd);
|
||||
Gdi::Window::add(hwnd);
|
||||
}
|
||||
disableDwmAttributes(hwnd);
|
||||
removeDropShadow(hwnd);
|
||||
Gdi::Window::add(hwnd);
|
||||
}
|
||||
|
||||
void onDestroyWindow(HWND hwnd)
|
||||
@ -173,20 +159,9 @@ namespace
|
||||
Gdi::Window::remove(hwnd);
|
||||
}
|
||||
|
||||
void onMenuSelect()
|
||||
{
|
||||
HWND menuWindow = FindWindow(reinterpret_cast<LPCSTR>(0x8000), nullptr);
|
||||
while (menuWindow)
|
||||
{
|
||||
RedrawWindow(menuWindow, nullptr, nullptr, RDW_INVALIDATE);
|
||||
menuWindow = FindWindowEx(nullptr, menuWindow, reinterpret_cast<LPCSTR>(0x8000), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void onWindowPosChanged(HWND hwnd)
|
||||
{
|
||||
const ATOM menuAtom = 0x8000;
|
||||
if (menuAtom == GetClassLongPtr(hwnd, GCW_ATOM))
|
||||
if (Gdi::MENU_ATOM == GetClassLongPtr(hwnd, GCW_ATOM))
|
||||
{
|
||||
SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
|
||||
}
|
||||
@ -196,7 +171,7 @@ namespace
|
||||
notifyFunc();
|
||||
}
|
||||
|
||||
if (isTopLevelNonLayeredWindow(hwnd))
|
||||
if (Gdi::Window::get(hwnd))
|
||||
{
|
||||
Gdi::Window::updateAll();
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "DDraw/ScopedThreadLock.h"
|
||||
#include "Gdi/Gdi.h"
|
||||
#include "Gdi/VirtualScreen.h"
|
||||
#include "Gdi/Window.h"
|
||||
@ -8,6 +9,13 @@ namespace
|
||||
{
|
||||
ATOM registerPresentationWindowClass();
|
||||
|
||||
ATOM getComboLBoxAtom()
|
||||
{
|
||||
WNDCLASS wc = {};
|
||||
static ATOM comboLBoxAtom = static_cast<ATOM>(GetClassInfo(nullptr, "ComboLBox", &wc));
|
||||
return comboLBoxAtom;
|
||||
}
|
||||
|
||||
ATOM getPresentationWindowClassAtom()
|
||||
{
|
||||
static ATOM atom = registerPresentationWindowClass();
|
||||
@ -33,37 +41,43 @@ namespace Gdi
|
||||
{
|
||||
Window::Window(HWND hwnd)
|
||||
: m_hwnd(hwnd)
|
||||
, m_presentationWindow(nullptr)
|
||||
, m_windowRect{ 0, 0, 0, 0 }
|
||||
, m_isUpdating(false)
|
||||
{
|
||||
m_presentationWindow = CreateWindowEx(
|
||||
WS_EX_LAYERED | WS_EX_TRANSPARENT,
|
||||
reinterpret_cast<const char*>(getPresentationWindowClassAtom()),
|
||||
nullptr,
|
||||
WS_DISABLED | WS_POPUP,
|
||||
0, 0, 1, 1,
|
||||
m_hwnd,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
SetLayeredWindowAttributes(m_presentationWindow, 0, 255, LWA_ALPHA);
|
||||
const ATOM atom = static_cast<ATOM>(GetClassLong(hwnd, GCW_ATOM));
|
||||
if (MENU_ATOM != atom && getComboLBoxAtom() != atom)
|
||||
{
|
||||
m_presentationWindow = CreateWindowEx(
|
||||
WS_EX_LAYERED | WS_EX_TRANSPARENT,
|
||||
reinterpret_cast<const char*>(getPresentationWindowClassAtom()),
|
||||
nullptr,
|
||||
WS_DISABLED | WS_POPUP,
|
||||
0, 0, 1, 1,
|
||||
m_hwnd,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
SetLayeredWindowAttributes(m_presentationWindow, 0, 255, LWA_ALPHA);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
Window* Window::add(HWND hwnd)
|
||||
bool Window::add(HWND hwnd)
|
||||
{
|
||||
auto it = s_windows.find(hwnd);
|
||||
if (it != s_windows.end())
|
||||
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())
|
||||
{
|
||||
return &it->second;
|
||||
s_windows.emplace(hwnd, std::make_shared<Window>(hwnd));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isPresentationWindow(hwnd))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &s_windows.emplace(hwnd, hwnd).first->second;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion)
|
||||
@ -97,22 +111,36 @@ namespace Gdi
|
||||
}
|
||||
}
|
||||
|
||||
Window* Window::get(HWND hwnd)
|
||||
std::shared_ptr<Window> Window::get(HWND hwnd)
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
auto it = s_windows.find(hwnd);
|
||||
return it != s_windows.end() ? &it->second : nullptr;
|
||||
return it != s_windows.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
HWND Window::getPresentationWindow() const
|
||||
{
|
||||
return m_presentationWindow ? m_presentationWindow : m_hwnd;
|
||||
}
|
||||
|
||||
Region Window::getVisibleRegion() const
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
return m_visibleRegion;
|
||||
}
|
||||
|
||||
RECT Window::getWindowRect() const
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
return m_windowRect;
|
||||
}
|
||||
|
||||
std::map<HWND, std::shared_ptr<Window>> Window::getWindows()
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
return s_windows;
|
||||
}
|
||||
|
||||
bool Window::isPresentationWindow(HWND hwnd)
|
||||
{
|
||||
return GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom();
|
||||
@ -120,11 +148,13 @@ namespace Gdi
|
||||
|
||||
void Window::remove(HWND hwnd)
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
s_windows.erase(hwnd);
|
||||
}
|
||||
|
||||
void Window::update()
|
||||
{
|
||||
DDraw::ScopedThreadLock lock;
|
||||
if (m_isUpdating)
|
||||
{
|
||||
return;
|
||||
@ -145,9 +175,16 @@ namespace Gdi
|
||||
newVisibleRegion &= VirtualScreen::getRegion();
|
||||
}
|
||||
|
||||
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);
|
||||
if (m_presentationWindow)
|
||||
{
|
||||
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)
|
||||
{
|
||||
ShowWindow(m_presentationWindow, SW_HIDE);
|
||||
}
|
||||
|
||||
std::swap(m_windowRect, newWindowRect);
|
||||
@ -160,23 +197,34 @@ namespace Gdi
|
||||
|
||||
void Window::updateAll()
|
||||
{
|
||||
for (auto& windowPair : s_windows)
|
||||
auto windows(getWindows());
|
||||
for (auto& windowPair : windows)
|
||||
{
|
||||
windowPair.second.update();
|
||||
windowPair.second->update();
|
||||
}
|
||||
|
||||
for (auto& windowPair : s_windows)
|
||||
for (auto& windowPair : windows)
|
||||
{
|
||||
if (!windowPair.second.m_invalidatedRegion.isEmpty())
|
||||
if (!windowPair.second->m_invalidatedRegion.isEmpty())
|
||||
{
|
||||
POINT clientOrigin = {};
|
||||
ClientToScreen(windowPair.first, &clientOrigin);
|
||||
windowPair.second.m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y);
|
||||
RedrawWindow(windowPair.first, nullptr, windowPair.second.m_invalidatedRegion,
|
||||
windowPair.second->m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y);
|
||||
RedrawWindow(windowPair.first, nullptr, windowPair.second->m_invalidatedRegion,
|
||||
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<HWND, Window> Window::s_windows;
|
||||
void Window::updateWindow()
|
||||
{
|
||||
RECT windowRect = {};
|
||||
GetWindowRect(m_hwnd, &windowRect);
|
||||
if (!EqualRect(&windowRect, &m_windowRect))
|
||||
{
|
||||
updateAll();
|
||||
}
|
||||
}
|
||||
|
||||
std::map<HWND, std::shared_ptr<Window>> Window::s_windows;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
@ -17,13 +18,16 @@ namespace Gdi
|
||||
Window(const Window&) = delete;
|
||||
Window& operator=(const Window&) = delete;
|
||||
|
||||
HWND getPresentationWindow() const;
|
||||
Region getVisibleRegion() const;
|
||||
RECT getWindowRect() const;
|
||||
void updateWindow();
|
||||
|
||||
static Window* add(HWND hwnd);
|
||||
static Window* get(HWND hwnd);
|
||||
static bool add(HWND hwnd);
|
||||
static std::shared_ptr<Window> get(HWND hwnd);
|
||||
static void remove(HWND hwnd);
|
||||
|
||||
static std::map<HWND, std::shared_ptr<Window>> getWindows();
|
||||
static bool isPresentationWindow(HWND hwnd);
|
||||
static void updateAll();
|
||||
|
||||
@ -38,6 +42,6 @@ namespace Gdi
|
||||
Region m_invalidatedRegion;
|
||||
bool m_isUpdating;
|
||||
|
||||
static std::map<HWND, Window> s_windows;
|
||||
static std::map<HWND, std::shared_ptr<Window>> s_windows;
|
||||
};
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace
|
||||
const int subKeyComp = lstrcmpiW(subKey.c_str(), rhs.subKey.c_str());
|
||||
if (subKeyComp < 0) { return true; }
|
||||
if (subKeyComp > 0) { return false; }
|
||||
return lstrcmpiW(value.c_str(), rhs.value.c_str());
|
||||
return lstrcmpiW(value.c_str(), rhs.value.c_str()) < 0;
|
||||
}
|
||||
|
||||
bool operator==(const RegistryKey& rhs) const
|
||||
|
Loading…
x
Reference in New Issue
Block a user