From 87139b31a30cf028ed55c14217a8f803d9f03945 Mon Sep 17 00:00:00 2001 From: anders_k Date: Tue, 16 Oct 2018 16:55:37 +0000 Subject: [PATCH] Created Window Spy/Info feature git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7019 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/Makensisw/SConscript | 1 + Contrib/Makensisw/makensisw.cpp | 13 +- Contrib/Makensisw/resource.h | 15 +- Contrib/Makensisw/resource.rc | 26 +++ Contrib/Makensisw/utils.cpp | 15 +- Contrib/Makensisw/utils.h | 1 + Contrib/Makensisw/wndspy.cpp | 276 ++++++++++++++++++++++++++++++++ 7 files changed, 341 insertions(+), 6 deletions(-) create mode 100644 Contrib/Makensisw/wndspy.cpp diff --git a/Contrib/Makensisw/SConscript b/Contrib/Makensisw/SConscript index b82ab211..29199db5 100644 --- a/Contrib/Makensisw/SConscript +++ b/Contrib/Makensisw/SConscript @@ -6,6 +6,7 @@ files = Split(""" utils.cpp version.cpp update.cpp + wndspy.cpp """) res = Split(""" diff --git a/Contrib/Makensisw/makensisw.cpp b/Contrib/Makensisw/makensisw.cpp index b85a7299..8ddfc384 100644 --- a/Contrib/Makensisw/makensisw.cpp +++ b/Contrib/Makensisw/makensisw.cpp @@ -613,6 +613,11 @@ INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam DialogBox(g_sdata.hInstance,MAKEINTRESOURCE(DLG_SETTINGS),g_sdata.hwnd,(DLGPROC)SettingsProc); return TRUE; } + case IDM_WNDSPY: + { + extern int ShowWndSpy(HWND hOwner); + return ShowWndSpy(g_sdata.hwnd); + } case IDM_TEST: case IDC_TEST: { @@ -836,6 +841,7 @@ INT_PTR CALLBACK AboutProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) switch(msg) { case WM_INITDIALOG: { + CenterOnParent(hwndDlg); HFONT fontnorm = CreateFont(13, FW_NORMAL, FIXED_PITCH|FF_DONTCARE, _T("Tahoma")), fontbold = CreateFont(13, FW_BOLD, FIXED_PITCH|FF_DONTCARE, _T("Tahoma")); if (!fontbold) { @@ -918,6 +924,7 @@ INT_PTR CALLBACK SettingsProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lPar switch(msg) { case WM_INITDIALOG: { + CenterOnParent(hwndDlg); for(int i = (int)COMPRESSOR_SCRIPT; i <= (int)COMPRESSOR_BEST; i++) SendDlgItemMessage(hwndDlg, IDC_COMPRESSOR, CB_ADDSTRING, 0, (LPARAM)compressor_display_names[i]); SendDlgItemMessage(hwndDlg, IDC_COMPRESSOR, CB_SETCURSEL, (WPARAM)g_sdata.default_compressor, (LPARAM)0); @@ -1086,9 +1093,8 @@ INT_PTR CALLBACK CompressorProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP switch(msg) { case WM_INITDIALOG: { - int i=0; - - for(i=(int)COMPRESSOR_SCRIPT; i<= (int)COMPRESSOR_BEST; i++) { + CenterOnParent(hwndDlg); + for(int i=(int)COMPRESSOR_SCRIPT; i <= (int)COMPRESSOR_BEST; i++) { SendDlgItemMessage(hwndDlg, IDC_COMPRESSOR, CB_ADDSTRING, 0, (LPARAM)compressor_display_names[i]); } SendDlgItemMessage(hwndDlg, IDC_COMPRESSOR, CB_SETCURSEL, (WPARAM)g_sdata.compressor, (LPARAM)0); @@ -1129,6 +1135,7 @@ INT_PTR CALLBACK SymbolSetProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lPa HWND hwndEdit; HKEY hKey; + CenterOnParent(hwndDlg); EnableWindow(GetDlgItem(hwndDlg, IDC_DEL), FALSE); if (OpenRegSettingsKey(hKey)) { HKEY hSubKey; diff --git a/Contrib/Makensisw/resource.h b/Contrib/Makensisw/resource.h index b4734a33..185052b4 100644 --- a/Contrib/Makensisw/resource.h +++ b/Contrib/Makensisw/resource.h @@ -44,6 +44,7 @@ #define IDB_TOOLBAR24D 130 #define IDB_BITMAP2 131 #define IDB_TOOLBAR24H 132 +#define DLG_WNDSPY 133 #define IDC_VERSION 200 #define IDC_LOGWIN 201 @@ -65,6 +66,15 @@ #define IDC_VALUE 238 #define IDC_COMPRESSOR 239 #define IDC_NAMES 240 +#define IDC_SPYDRAG 220 +#define IDC_HWND 221 +#define IDC_WNDID 222 +#define IDC_WNDCLASS 223 +#define IDC_WNDUSERDATA 224 +#define IDC_WNDSTYLE 225 +#define IDC_WNDSIZE 226 +#define IDC_WNDINFO 227 +#define IDC_WNDDPI 228 #define IDM_MRU_FILE 6000 #define IDM_CMDBASE 500 @@ -104,13 +114,14 @@ #define IDM_RECOMPILE_TEST 533 #define IDM_CANCEL 534 #define IDM_SETTINGS 535 +#define IDM_WNDSPY 536 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 133 -#define _APS_NEXT_COMMAND_VALUE 536 +#define _APS_NEXT_RESOURCE_VALUE 134 +#define _APS_NEXT_COMMAND_VALUE 537 #define _APS_NEXT_CONTROL_VALUE 241 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/Contrib/Makensisw/resource.rc b/Contrib/Makensisw/resource.rc index 52c1a9ba..633da7a1 100644 --- a/Contrib/Makensisw/resource.rc +++ b/Contrib/Makensisw/resource.rc @@ -110,6 +110,8 @@ BEGIN BEGIN MENUITEM "&Settings..\tCtrl+S", IDM_SETTINGS MENUITEM "", -1, MFT_SEPARATOR + MENUITEM "&Window Info", IDM_WNDSPY + MENUITEM "", -1, MFT_SEPARATOR MENUITEM "Clear Recent &Files List", IDM_CLEAR_MRU_LIST END POPUP "&Help", IDM_HELP @@ -242,6 +244,30 @@ BEGIN PUSHBUTTON "&Delete",IDC_DEL,6,102,50,14 END +DLG_WNDSPY DIALOGEX 0, 0, 236, 120 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Window Info" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "", IDC_SPYDRAG, STATIC, SS_ICON | SS_NOTIFY | SS_CENTERIMAGE | SS_SUNKEN | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 7, 20 , 20 + LTEXT "Drag the finder over a window and then release the mouse...", IDC_STATIC , 39, 7, 190, 20 + CONTROL "Id:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 34, 32, 10 + CONTROL "", IDC_WNDID, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 34, 190, 10 + CONTROL "HWND:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 44, 32, 10 + CONTROL "", IDC_HWND, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 44, 190, 10 + CONTROL "Class:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 54, 32, 10 + CONTROL "", IDC_WNDCLASS, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 54, 190, 10 + CONTROL "Data:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 64, 32, 10 + CONTROL "", IDC_WNDUSERDATA, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, 39, 64, 190, 10 + CONTROL "Style:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 74, 32, 10 + CONTROL "", IDC_WNDSTYLE, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 74, 190, 10 + CONTROL "Size:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 84, 32, 10 + CONTROL "", IDC_WNDSIZE, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 84, 190, 10 + CONTROL "Info:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 94, 32, 10 + CONTROL "", IDC_WNDINFO, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 94, 190, 10 + CONTROL "DPI:", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP , 7, 104, 32, 10 + CONTROL "", IDC_WNDDPI, EDIT, ES_LEFT | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL , 39, 104, 190, 10 +END ///////////////////////////////////////////////////////////////////////////// // diff --git a/Contrib/Makensisw/utils.cpp b/Contrib/Makensisw/utils.cpp index a350d24f..d25045d9 100644 --- a/Contrib/Makensisw/utils.cpp +++ b/Contrib/Makensisw/utils.cpp @@ -180,6 +180,19 @@ void ErrorMessage(HWND hwnd,const TCHAR *str) { LogMessage(hwnd,buf); } +static void CenterOnParent(HWND hwnd, HWND hParent) +{ + RECT r; + GetWindowRect(hwnd, &r); + UINT w = (r.right - r.left), h = (r.bottom - r.top), swp = SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE; + if (GetWindowRect(hParent, &r)) + SetWindowPos(hwnd, 0, r.left + ((r.right - r.left)/2) - (w/2), r.top + ((r.bottom - r.top)/2) - (h/2), 0, 0, swp); +} +void CenterOnParent(HWND hwnd) +{ + CenterOnParent(hwnd, GetWindow(hwnd, GW_OWNER)); +} + void SetDialogFocus(HWND hDlg, HWND hCtl) { //blogs.msdn.com/b/oldnewthing/archive/2004/08/02/205624.aspx @@ -956,7 +969,7 @@ bool FileExists(const TCHAR *fname) bool OpenUrlInDefaultBrowser(HWND hwnd, LPCSTR Url) { - return (int)(INT_PTR) ShellExecuteA(hwnd, NULL , Url, NULL, NULL, SW_SHOWNORMAL) > 32; + return (int)(INT_PTR) ShellExecuteA(hwnd, NULL, Url, NULL, NULL, SW_SHOWNORMAL) > 32; } HMENU FindSubMenu(HMENU hMenu, UINT uId) diff --git a/Contrib/Makensisw/utils.h b/Contrib/Makensisw/utils.h index 0e6fcb98..bfce44c3 100644 --- a/Contrib/Makensisw/utils.h +++ b/Contrib/Makensisw/utils.h @@ -51,6 +51,7 @@ void CopyToClipboard(HWND hwnd); void ClearLog(HWND hwnd); void LogMessage(HWND hwnd,const TCHAR *str); void ErrorMessage(HWND hwnd,const TCHAR *str); +void CenterOnParent(HWND hwnd); void SetDialogFocus(HWND hDlg, HWND hCtl); // Use this and not SetFocus()! #define DisableItems(hwnd) EnableDisableItems(hwnd, 0) #define EnableItems(hwnd) EnableDisableItems(hwnd, 1) diff --git a/Contrib/Makensisw/wndspy.cpp b/Contrib/Makensisw/wndspy.cpp new file mode 100644 index 00000000..62ea24aa --- /dev/null +++ b/Contrib/Makensisw/wndspy.cpp @@ -0,0 +1,276 @@ +// Copyright (C) 2018 Anders Kjersem +// +// This file is a part of NSIS. +// +// Licensed under the zlib/libpng license (the "License"); +// you may not use this file except in compliance with the License. +// +// Licence details can be found in the file COPYING. +// +// This software is provided 'as-is', without any express or implied +// warranty. + +#include +#include +#include "makensisw.h" +#include "utils.h" +#include "resource.h" + +static FARPROC GetModProc(LPCSTR Mod, LPCSTR Func) { return GetProcAddress(LoadLibraryA(Mod), Func); } +#define InitializeApiFuncWithFallback(mn, fn) { FARPROC f = GetModProc((mn), (#fn)); g_##fn = Compat_##fn; if (f) (FARPROC&) g_##fn = f; } +#define InitializeApiFunc(mn, fn) ( (FARPROC&)(g_##fn) = GetModProc((mn), (#fn)) ) +#define CallApiFunc(fn) ( g_##fn ) +#define HasApiFunc(fn) ( !!(g_##fn) ) + +INT_PTR(WINAPI*g_SetThreadDpiAwarenessContext)(INT_PTR); +UINT(WINAPI*g_GetDpiForWindow)(HWND); + +typedef struct _DPI { + enum { a_invalid = -1, a_unaware = 0, a_system = 1, a_pm = 2 }; + enum { ac_invalid = 0, ac_unaware = -1, ac_system = -2, ac_pm1 = -3, ac_pm2 = -4 }; + static inline INT_PTR SetThreadDpiAwarenessContext(INT_PTR a1) { return g_SetThreadDpiAwarenessContext(a1); } + static inline UINT GetDpiForWindow(HWND a1) { return HasApiFunc(GetDpiForWindow) ? CallApiFunc(GetDpiForWindow)(a1) : 0; } +} DPI; + +static INT_PTR WINAPI Compat_SetThreadDpiAwarenessContext(INT_PTR AC) +{ + return NULL; +} + +static void InitializeDpiApi() +{ + InitializeApiFuncWithFallback("USER32", SetThreadDpiAwarenessContext); + InitializeApiFunc("USER32", GetDpiForWindow); +} + +static HWND GetParentWindow(HWND hWnd) +{ + HWND hParent = GetAncestor(hWnd, GA_PARENT); // Parent but NOT owner. + return hParent == GetParent(hWnd) ? hParent : NULL; // Filter away GetDesktopWindow(). +} + +typedef struct _DIALOGDATA { + BOOL Dragging; + HWND hWndTarget; + int DialogAwarenessContext; + static struct _DIALOGDATA* Get(HWND hDlg) { return (DIALOGDATA*) GetWindowLongPtr(hDlg, DWLP_USER); } +} DIALOGDATA; + +typedef struct { + HWND hWnd; + POINT pt; + ULONG Area; + bool IncludeHidden; + void Init(POINT pt, bool IncludeHidden) { Area = 0, Area = ~Area, this->pt = pt, this->IncludeHidden = IncludeHidden; } +} FINDCHILDDATA; + +static BOOL CALLBACK FindChildWindowFromPointProc(HWND hWnd, LPARAM LParam) +{ + RECT r; + FINDCHILDDATA*pFCD = (FINDCHILDDATA*) LParam; + if (GetWindowRect(hWnd, &r) && PtInRect(&r, pFCD->pt)) // TODO: Region? + { + ULONG area = (r.right - r.left) * (r.bottom - r.top); + if (area < pFCD->Area && (pFCD->IncludeHidden || IsWindowVisible(hWnd))) + pFCD->Area = area, pFCD->hWnd = hWnd; + } + return TRUE; +} + +static HWND FindChildWindowFromPoint(HWND hWnd, FINDCHILDDATA*pFCD) +{ + HWND hParent = GetParentWindow(hWnd), hOrg; + if (!hParent) + hParent = hWnd; +recurse: + hOrg = pFCD->hWnd = hWnd; + EnumChildWindows(hParent, FindChildWindowFromPointProc, (LPARAM) pFCD); + if (hOrg && hOrg != pFCD->hWnd) + { + hWnd = hParent = pFCD->hWnd; + goto recurse; + } + return pFCD->hWnd; +} + +static HWND GetChildWindowFromPointHelper(POINT pt) +{ + const bool includeHidden = false; + HWND hWnd = WindowFromPoint(pt), hWnd2; + if (!hWnd) + return hWnd; + while(!includeHidden && hWnd && !IsWindowVisible(hWnd)) + if ((hWnd2 = GetParentWindow(hWnd))) + hWnd = hWnd2; +#if 0 + for (HWND hWndChild;;) + { + POINT childpt = pt; + HWND hWndParent = GetParent(hWnd); + ScreenToClient(hWndParent, &childpt); + if (!(hWndChild = RealChildWindowFromPoint(hWndParent, childpt)) || hWndChild == hWnd) + break; + hWnd = hWndChild; + } +#else + FINDCHILDDATA fcd; + fcd.Init(pt, includeHidden); + hWnd = FindChildWindowFromPoint(hWnd, &fcd); +#endif + return hWnd; +} + +static BOOL IsHung(HWND hWnd) +{ + if (sizeof(void*) > 4 || sizeof(TCHAR) > 1) + { + return IsHungAppWindow(hWnd); + } + else + { + static FARPROC g_func = GetProcAddress(LoadLibraryA("USER32"), "IsHungAppWindow"); + if (g_func) return ((BOOL(WINAPI*)(HWND))g_func)(hWnd); + LRESULT mr, rv = SendMessageTimeout(hWnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 500, &mr); + return rv == 0; + } +} + +static void SetDragSourceImage(HWND hDlg, INT_PTR Dragging = false) +{ + HCURSOR hCur = Dragging ? NULL : LoadCursor(NULL, MAKEINTRESOURCE(IDC_CROSS)); + SendDlgItemMessage(hDlg, IDC_SPYDRAG, STM_SETIMAGE, IMAGE_CURSOR, (LPARAM) hCur); +} + +static void ShowWindowInfo(HWND hDlg, HWND hWnd) +{ + const TCHAR *strFmtPtr = sizeof(void*) > 4 ? _T("%#.16I64x") : _T("%#.8x"); + const TCHAR *strFmtPtr2 = sizeof(void*) > 4 ? _T("%#.16I64x & %#.16I64x") : _T("%#.8x & %#.8x"); + const TCHAR *strIntPtr = sizeof(void*) > 4 ? _T("%I64d") : _T("%d"); + TCHAR buf[300]; + LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE); + + wsprintf(buf, strIntPtr, GetWindowLongPtr(hWnd, GWLP_ID)); + SetDlgItemText(hDlg, IDC_WNDID, buf); + + wsprintf(buf, strFmtPtr, hWnd); + SetDlgItemText(hDlg, IDC_HWND, buf); + + if (!GetClassName(hWnd, buf, COUNTOF(buf))) *buf = _T('\0'); + SetDlgItemText(hDlg, IDC_WNDCLASS, buf); + + wsprintf(buf, strFmtPtr2, GetWindowLongPtr(hWnd, GWLP_USERDATA), GetWindowLongPtr(hWnd, DWLP_USER)); + SetDlgItemText(hDlg, IDC_WNDUSERDATA, buf); + + wsprintf(buf, strFmtPtr2, (LONG_PTR) style, GetWindowLongPtr(hWnd, GWL_EXSTYLE)); + SetDlgItemText(hDlg, IDC_WNDSTYLE, buf); + + RECT rw, rc; + GetWindowRect(hWnd, &rw), GetClientRect(hWnd, &rc); + wsprintf(buf, _T("%dx%d..%dx%d \x2248 %dx%d (%dx%d)"), rw.left, rw.top, rw.right, rw.bottom, rw.right - rw.left, rw.bottom - rw.top, rc.right - rc.left, rc.bottom - rc.top); // '\x2245' is not present on XP + SetDlgItemText(hDlg, IDC_WNDSIZE, hWnd ? buf : NULL); + + *buf = _T('\0'); + if (IsWindowUnicode(hWnd)) lstrcat(buf, _T("Unicode")); + if (IsWindowVisible(hWnd)) lstrcat(*buf ? lstrcat(buf, _T(", ")) : buf, _T("Visible")); // IsWindowVisible is not exactly the same as WS_VISIBLE + if (!(style & WS_DISABLED)) lstrcat(*buf ? lstrcat(buf, _T(", ")) : buf, _T("Enabled")); + if (IsHung(hWnd)) lstrcat(*buf ? lstrcat(buf, _T(", ")) : buf, _T("Hung")); + SetDlgItemText(hDlg, IDC_WNDINFO, hWnd ? buf : NULL); + + UINT dpi = DPI::GetDpiForWindow(hWnd); + wsprintf(buf, dpi ? _T("%u") : _T("?"), dpi); + SetDlgItemText(hDlg, IDC_WNDDPI, hWnd ? buf : NULL); +} + +static INT_PTR CALLBACK SpyDlgProc(HWND hDlg, UINT Msg, WPARAM WParam, LPARAM LParam) +{ + DIALOGDATA*pDD = DIALOGDATA::Get(hDlg); + switch(Msg) + { + case WM_INITDIALOG: + SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) (pDD = (DIALOGDATA*) LParam)); + CenterOnParent(hDlg); + // On >= 10.1703 we are PMv2 and Windows scales our dialog and child controls. + // On >= 10.1607 && < 10.1703 we are System aware but try to upgrade this thread to + // PMv1 to reduce compatibility handling in other USER32 functions. We don't want + // the dialog HWND to be PMv1 because we are not going to reposition our controls. + // On < 10.1607 we only have the process awareness (not ideal). + if (pDD->DialogAwarenessContext > DPI::ac_pm2) // Is the dialog AC < PMv2? Note: The canonical AC numbers are negative. + DPI::SetThreadDpiAwarenessContext(DPI::ac_pm1); + SendMessage(hDlg, WM_CAPTURECHANGED, 0, 0); + break; + case WM_CLOSE: close: + return EndDialog(hDlg, 0); + case WM_COMMAND: + switch(WParam) + { + case IDCANCEL: + goto close; + case MAKELONG(IDC_SPYDRAG, STN_CLICKED): + SetCapture(hDlg); + pDD->hWndTarget = 0; + pDD->Dragging++; + SetDragSourceImage(hDlg, (INT_PTR) hDlg); + SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_CROSS))); + break; + } + break; + case WM_MOUSEMOVE: + if (pDD->Dragging) + { + POINT pt; + GetCursorPos(&pt); + HWND hWnd = GetChildWindowFromPointHelper(pt); + if (hWnd == pDD->hWndTarget) + break; + + pDD->hWndTarget = hWnd; + if (GetAncestor(hWnd, GA_ROOT) == hDlg) + hWnd = 0; + + ShowWindowInfo(hDlg, hWnd); + } + break; + case WM_LBUTTONUP: + ReleaseCapture(); + break; + case WM_CAPTURECHANGED: + SetFocus(GetDlgItem(hDlg, IDC_SPYDRAG)); + pDD->Dragging = 0; + SetDragSourceImage(hDlg, FALSE); + break; + } + return FALSE; +} + +struct ScopedThreadDpiAwarenessContext { // Note: Assumes InitializeDpiApi() has been called! + struct List { + List() : m_l(0) {} + List operator <<(INT_PTR in) { List r = *this; r.m_l |= (1 << (in < 0 ? -in : in)); return r; } + UINT GetBits() const { return m_l; } + UINT m_l; + }; + ScopedThreadDpiAwarenessContext(List ACList) : m_OrgAC(NULL) + { + for (UINT s = 4, list = ACList.GetBits(); m_AC = -(int)s; --s) + if ((1 << s) & list) + if ((m_OrgAC = DPI::SetThreadDpiAwarenessContext((INT_PTR) m_AC))) + break; + } + ~ScopedThreadDpiAwarenessContext() + { + DPI::SetThreadDpiAwarenessContext(m_OrgAC); + } + int GetCanonicalActiveAwarenessContext() const { return m_AC; } + INT_PTR m_OrgAC; + int m_AC; // Canonical "active" DPI_AWARENESS_CONTEXT +}; + +int ShowWndSpy(HWND hOwner) +{ + InitializeDpiApi(); + ScopedThreadDpiAwarenessContext::List aclist; + ScopedThreadDpiAwarenessContext stdac(aclist << DPI::ac_pm2 << DPI::ac_system); + DIALOGDATA dd; + dd.DialogAwarenessContext = stdac.GetCanonicalActiveAwarenessContext(); + return DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(DLG_WNDSPY), hOwner, SpyDlgProc, (LPARAM) &dd); +}