Created Window Spy/Info feature

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7019 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2018-10-16 16:55:37 +00:00
parent 51d7471805
commit 87139b31a3
7 changed files with 341 additions and 6 deletions

View file

@ -6,6 +6,7 @@ files = Split("""
utils.cpp
version.cpp
update.cpp
wndspy.cpp
""")
res = Split("""

View file

@ -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;

View file

@ -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

View file

@ -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
/////////////////////////////////////////////////////////////////////////////
//

View file

@ -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)

View file

@ -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)

View file

@ -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 <windows.h>
#include <windowsx.h>
#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);
}