├── src
├── Readme.txt
├── resource
│ ├── app.ico
│ ├── dots.ico
│ ├── down.ico
│ ├── less.ico
│ ├── more.ico
│ ├── WinSpy.rc
│ ├── check1.bmp
│ ├── check2.bmp
│ ├── cursor1.cur
│ ├── enter.ico
│ ├── selbox.png
│ ├── selbox.psd
│ ├── selbox2.png
│ ├── dragtool1.bmp
│ ├── dragtool2.bmp
│ ├── thumbtack.bmp
│ ├── treeicons.bmp
│ ├── treeicons-cloaked.bmp
│ ├── treeicons-hidden.bmp
│ ├── winspy.exe.manifest
│ └── resource.h
├── Poster.h
├── CaptureWindow.h
├── WindowFromPointEx.h
├── InjectThread.h
├── BitmapButton.h
├── RegHelper.h
├── FindTool.h
├── History.txt
├── Utils.h
├── DisplayPropInfo.c
├── FindToolTrans.c
├── TabCtrlUtils.c
├── DisplayScrollInfo.c
├── RegHelper.c
├── FlashWindow.cpp
├── DisplayWindowInfo.c
├── PropertyEdit.c
├── StaticCtrl.c
├── InjectThread.c
├── WindowFromPointEx.c
├── FunkyList.c
├── LoadPNG.cpp
├── GetRemoteWindowInfo.c
├── EditSize.c
├── winspy.vcxproj.filters
├── Options.c
├── WinSpyCommand.c
├── DisplayGeneralInfo.c
├── StyleEdit.c
├── CaptureWindow.c
├── FindTool.c
├── BitmapButton.c
├── winspy.vcxproj
├── DisplayDpiInfo.c
├── DisplayProcessInfo.c
└── WinSpy.h
├── .editorconfig
├── .gitignore
├── README.md
├── LICENCE.TXT
├── .github
└── workflows
│ └── msbuild.yml
└── WinSpy.sln
/src/Readme.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/Readme.txt
--------------------------------------------------------------------------------
/src/resource/app.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/app.ico
--------------------------------------------------------------------------------
/src/resource/dots.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/dots.ico
--------------------------------------------------------------------------------
/src/resource/down.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/down.ico
--------------------------------------------------------------------------------
/src/resource/less.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/less.ico
--------------------------------------------------------------------------------
/src/resource/more.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/more.ico
--------------------------------------------------------------------------------
/src/resource/WinSpy.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/WinSpy.rc
--------------------------------------------------------------------------------
/src/resource/check1.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/check1.bmp
--------------------------------------------------------------------------------
/src/resource/check2.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/check2.bmp
--------------------------------------------------------------------------------
/src/resource/cursor1.cur:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/cursor1.cur
--------------------------------------------------------------------------------
/src/resource/enter.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/enter.ico
--------------------------------------------------------------------------------
/src/resource/selbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/selbox.png
--------------------------------------------------------------------------------
/src/resource/selbox.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/selbox.psd
--------------------------------------------------------------------------------
/src/resource/selbox2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/selbox2.png
--------------------------------------------------------------------------------
/src/resource/dragtool1.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/dragtool1.bmp
--------------------------------------------------------------------------------
/src/resource/dragtool2.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/dragtool2.bmp
--------------------------------------------------------------------------------
/src/resource/thumbtack.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/thumbtack.bmp
--------------------------------------------------------------------------------
/src/resource/treeicons.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/treeicons.bmp
--------------------------------------------------------------------------------
/src/resource/treeicons-cloaked.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/treeicons-cloaked.bmp
--------------------------------------------------------------------------------
/src/resource/treeicons-hidden.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BissetJ/winspy/HEAD/src/resource/treeicons-hidden.bmp
--------------------------------------------------------------------------------
/src/Poster.h:
--------------------------------------------------------------------------------
1 | #ifndef POSTER_INCLUDED
2 | #define POSTER_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | BOOL IsPosterMessage(LPMSG lpMsg);
9 |
10 | #ifdef __cplusplus
11 | }
12 | #endif
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/src/CaptureWindow.h:
--------------------------------------------------------------------------------
1 | #ifndef CAPTURE_WINDOW_INCLUDED
2 | #define CAPTURE_WINDOW_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | BOOL CaptureWindow(HWND hwndOwner, HWND hwnd);
9 |
10 | #ifdef __cplusplus
11 | }
12 | #endif
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/src/WindowFromPointEx.h:
--------------------------------------------------------------------------------
1 | #ifndef WINDOWFROMPOINT_INCLUDED
2 | #define WINDOWFROMPOINT_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | HWND WindowFromPointEx(POINT pt, BOOL fTopLevel, BOOL fAllowHidden);
9 |
10 | #ifdef __cplusplus
11 | }
12 | #endif
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | # Windows-style newlines with a newline ending every file
5 | [*]
6 | end_of_line = crlf
7 | insert_final_newline = true
8 |
9 | # C/C++ code formatting
10 | [*.{c,cpp,h}]
11 | indent_style = space
12 | cpp_space_pointer_reference_alignment = right
13 |
--------------------------------------------------------------------------------
/src/InjectThread.h:
--------------------------------------------------------------------------------
1 | #ifndef INJECT_THREAD
2 | #define INJECT_THREAD
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | DWORD InjectRemoteThread(HWND hwnd, LPTHREAD_START_ROUTINE lpCode, DWORD_PTR cbCodeSize, LPVOID lpData, DWORD cbDataSize, DWORD cbInput);
9 |
10 | #ifdef __cplusplus
11 | }
12 | #endif
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/src/BitmapButton.h:
--------------------------------------------------------------------------------
1 | #ifndef BITMAPBUTTON_INCLUDED
2 | #define BITMAPBUTTON_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | BOOL DrawBitmapButton(DRAWITEMSTRUCT *dis);
9 |
10 | void MakeBitmapButton(HWND hwnd, UINT uIconId);
11 | void MakeDlgBitmapButton(HWND hwndDlg, UINT uCtrlId, UINT uIconId);
12 |
13 | #ifdef __cplusplus
14 | }
15 | #endif
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore all files of types:
2 | *.aps
3 | *.bsc
4 | *.cache
5 | *.exe
6 | *.idb
7 | *.map
8 | *.lib
9 | *.ncb
10 | *.obj
11 | *.old
12 | *.opensdf
13 | *.pdb
14 | *.res
15 | *.sbr
16 | *.sdf
17 | *.user
18 | *.zip
19 | *.VC.db
20 | *.suo
21 |
22 | # ignore the following files:
23 | BuildLog.htm
24 |
25 | # ignore the following directories - they can occur
26 | # anywhere under the repo
27 | Unicode_Debug/
28 | Unicode_Release/
29 | Debug/
30 | Release/
31 | ipch/
32 |
33 | .vs
34 |
--------------------------------------------------------------------------------
/src/RegHelper.h:
--------------------------------------------------------------------------------
1 | #ifndef _REGHELPER_INCLUDED
2 | #define _REGHELPER_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | LONG GetSettingInt(HKEY hkey, PCWSTR szKeyName, LONG nDefault);
9 | BOOL GetSettingBool(HKEY hkey, PCWSTR szKeyName, BOOL nDefault);
10 | LONG GetSettingStr(HKEY hkey, PCWSTR szKeyName, PCWSTR szDefault, PWSTR szReturnStr, DWORD nSize);
11 | LONG GetSettingBinary(HKEY hkey, PCWSTR szKeyName, void *buf, ULONG nNumBytes);
12 |
13 | LONG WriteSettingInt(HKEY hkey, PCWSTR szKeyName, LONG nValue);
14 | LONG WriteSettingBool(HKEY hkey, PCWSTR szKeyName, BOOL nValue);
15 | LONG WriteSettingStr(HKEY hkey, PCWSTR szKeyName, PCWSTR szString);
16 | LONG WriteSettingBinary(HKEY hkey, PCWSTR szKeyName, void *buf, UINT nNumBytes);
17 |
18 | #ifdef __cplusplus
19 | }
20 | #endif
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WinSpy++
2 | --------
3 |
4 | WinSpy++ is a programmer's utility for inspecting and modifying window properties of any Windows program.
5 |
6 | Building WinSpy++
7 | -----------------
8 |
9 | WinSpy++ requires Visual Studio 2015 (with "MFC" and "Windows XP support for C++" features installed), and supports Win32 and Win64 builds. Use the IDE to build WinSpy++, or the build/build.bat command-line script (requires Ruby) to build and package a zip file for distribution.
10 |
11 | About the fork
12 | --------------
13 |
14 | The fork introduces the following improvements to the original WinSpy++ 1.7:
15 |
16 | * Visual Studio 2015.
17 | * Complete Win64 support.
18 | * Window properties editor.
19 | * Autoupdate button, which refreshes the window properties periodically.
20 | * More extended styles.
21 | * lots of bug fixes and small improvements.
22 | * Broadcaster feature to broadcast send/post messages.
23 |
--------------------------------------------------------------------------------
/src/resource/winspy.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 | WinSpy
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 | true
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/FindTool.h:
--------------------------------------------------------------------------------
1 | #ifndef FINDTOOL_INCLUDED
2 | #define FINDTOOL_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | typedef UINT(CALLBACK * WNDFINDPROC) (HWND hwndTool, UINT uCode, HWND hwnd);
9 |
10 | //
11 | // uCode can be one of these values:
12 | //
13 | #define WFN_BEGIN 0 // tool is about to become active. hwnd(0)
14 | #define WFN_SELCHANGED 1 // sent when tool moves from window-window.
15 | #define WFN_END 2 // sent when final window has been selected.
16 | #define WFN_CANCELLED 3 // Tool cancelled. hwnd is not valid (0)
17 |
18 | #define WFN_CTRL_DOWN 4 // key was pressed
19 | #define WFN_CTRL_UP 5 // key was released
20 | #define WFN_SHIFT_DOWN 6 // key was pressed
21 | #define WFN_SHIFT_UP 7 // key was released
22 |
23 | #define WFN_CAPTURE 8 // Capture key pressed
24 |
25 | BOOL MakeFinderTool(HWND hwnd, WNDFINDPROC wfp);
26 |
27 | void FlashWindowBorder(HWND hwnd);
28 |
29 | #ifdef __cplusplus
30 | }
31 | #endif
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/LICENCE.TXT:
--------------------------------------------------------------------------------
1 | Copyright (C) 2012 James Brown
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/src/History.txt:
--------------------------------------------------------------------------------
1 | WinSpy++ Version History
2 | --------------------------
3 |
4 | Version 1.7.1 - May 2007 (Current Version)
5 | * BUG FIXES *
6 | - Moved 'winspy' system-menu items to appear before the Close item
7 | - Fixed 'resolve' window procedure hyperlink
8 |
9 | Version 1.7 - Jun 2006
10 | * NEW FEATURES *
11 | - Finally supports XP and Vista themes
12 | - Window-hierarchy now groups windows for each process
13 | - Static text-labels changed to read-only edit-boxes
14 |
15 | * BUG FIXES *
16 | - Fixed resize issue on first invokation
17 |
18 | Version 1.6 - Apr 2003
19 | * NEW FEATURES *
20 | - Multi monitor support
21 | - Changed dialog font to "MS Shell Dlg"
22 |
23 | * BUG FIXES *
24 | - Now runs properly under Win9x
25 |
26 | Version 1.5 - Jan 2002
27 | * NEW FEATURES *
28 | - Can select ANY window in the system
29 | - Improved user interface.
30 | - Full window dragging
31 | - Added accelerator keys
32 | - Added options dialog
33 | - Preserves settings through registry
34 | - Pin window to side of screen
35 | - Added Process tab - moved Scrollbar data to Properties tab
36 | - Show/Hide/Enable/Disable/Close any window
37 | - Modify window's caption
38 | - Modify window styles
39 | - Move/Size windows
40 | - Retrieve password text from password protected text-boxes
41 | - Close/Terminate processes
42 | - Can now toggle layout/show/hide main-window whilst selecting
43 | - Added Refresh, Locate and Flash buttons to window-tree
44 | - Added control icons to window tree.
45 |
46 | * BUG FIXES *
47 | - Fixed small style decoding problem (occurred in a few places)
48 | - Main window now sizes properly according to window settings
49 |
50 | Version 1.1 - 1.4
51 | - Unreleased interim versions
52 |
53 | Version 1.0 - Apr 2001
54 | * INITIAL RELEASE *
--------------------------------------------------------------------------------
/src/Utils.h:
--------------------------------------------------------------------------------
1 | #ifndef UTILS_INCLUDED
2 | #define UTILS_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | BOOL StrBeginsWith(PCWSTR pcsz, PCWSTR pcszPrefix);
9 |
10 | UINT AddStyle(HWND hwnd, UINT style);
11 | UINT AddDlgItemStyle(HWND hwnd, UINT nCtrlId, UINT style);
12 | UINT DelStyle(HWND hwnd, UINT style);
13 | UINT DelDlgItemStyle(HWND hwnd, UINT nCtrlId, UINT style);
14 | BOOL EnableDlgItem(HWND hwnd, UINT nCtrlId, BOOL fEnabled);
15 | BOOL ShowDlgItem(HWND hwnd, UINT nCtrlId, DWORD dwShowCmd);
16 | void SetDlgItemTextEx(HWND hwndDlg, UINT nCtrlId, PCWSTR pcsz);
17 | void SetDlgItemTextExA(HWND hwndDlg, UINT nCtrlId, PCSTR pcsz);
18 |
19 | void FormatDlgItemText(HWND hwndDlg, UINT id, _Printf_format_string_ PCWSTR pcszFormat, ...);
20 |
21 | int WINAPI GetRectHeight(RECT *rect);
22 | int WINAPI GetRectWidth(RECT *rect);
23 |
24 | DWORD_PTR GetDlgItemBaseInt(HWND hwnd, UINT ctrlid, int base);
25 | DWORD_PTR _tstrtoib16(PCWSTR pszHexStr);
26 | BOOL EnableDialogTheme(HWND hwnd);
27 |
28 | BOOL EnableDebugPrivilege();
29 |
30 | WCHAR *GetVersionString(WCHAR *szFileName, WCHAR *szValue, WCHAR *szBuffer, ULONG nLength);
31 |
32 | BOOL ProcessArchMatches(HWND hwnd);
33 | WORD GetProcessorArchitecture();
34 |
35 | HWND GetRealParent(HWND hWnd);
36 |
37 | BOOL CopyTextToClipboard(HWND hWnd, WCHAR *psz);
38 |
39 | HBITMAP LoadPNGImage(UINT id, void **bits);
40 |
41 | HBITMAP ExpandNineGridImage(SIZE outputSize, HBITMAP hbmSrc, RECT edges);
42 |
43 | void UpdateLayeredWindowContent(HWND hwnd, RECT rc, HBITMAP hbmp, BYTE alpha);
44 |
45 | BOOL IsWindowsFormsClassName(PCWSTR pcszClass);
46 | void ExtractWindowsFormsInnerClassName(PWSTR pszName);
47 |
48 | RECT GetControlRect(HWND hwndParent, HWND hwnd);
49 | void SetControlRect(HWND hwnd, RECT* prc);
50 |
51 | #ifdef __cplusplus
52 | }
53 | #endif
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/src/DisplayPropInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayPropInfo.c
3 | // Copyright (c) 2002 by J Brown
4 | // Freeware
5 | //
6 | // Fill the properties-tab-pane with class info for the
7 | // specified window
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | #include "resource.h"
13 |
14 | //
15 | // Called once for each window property
16 | //
17 | BOOL CALLBACK PropEnumProcEx(HWND hwnd, PWSTR lpszString, HANDLE hData, ULONG_PTR dwUser)
18 | {
19 | UNREFERENCED_PARAMETER(hwnd);
20 | HWND hwndList = (HWND)dwUser;
21 | WCHAR ach[256];
22 | LVITEM lvitem;
23 | int index;
24 |
25 | lvitem.mask = LVIF_TEXT | LVIF_PARAM;
26 | lvitem.iItem = 0;
27 | lvitem.iSubItem = 0;
28 | lvitem.lParam = 0;
29 |
30 | // check that lpszString is a valid string, and not an ATOM in disguise
31 | if (((ULONG_PTR)lpszString & ~(ULONG_PTR)0xFFFF) == 0)
32 | {
33 | swprintf_s(ach, ARRAYSIZE(ach), L"%04hX (Atom)", (ATOM)(intptr_t)lpszString);
34 | lvitem.pszText = ach;
35 |
36 | lvitem.lParam = (LPARAM)lpszString;
37 | }
38 | else
39 | lvitem.pszText = lpszString;
40 |
41 | index = ListView_InsertItem(hwndList, &lvitem);
42 | if (index != -1)
43 | {
44 | swprintf_s(ach, ARRAYSIZE(ach), L"%p", (void*)hData);
45 | ListView_SetItemText(hwndList, index, 1, ach);
46 | }
47 |
48 | return TRUE;
49 | }
50 |
51 | //
52 | // Display the window properties (SetProp API)
53 | //
54 | void EnumWindowProps(HWND hwnd, HWND hwndList)
55 | {
56 | ListView_DeleteAllItems(hwndList);
57 | if (hwnd == 0) return;
58 | EnumPropsEx(hwnd, PropEnumProcEx, (ULONG_PTR)hwndList);
59 | }
60 |
61 | void UpdatePropertyTab(HWND hwnd)
62 | {
63 | EnumWindowProps(hwnd, GetDlgItem(WinSpyTab[PROPERTY_TAB].hwnd, IDC_LIST1));
64 |
65 | UpdateScrollbarInfo(hwnd);
66 | }
67 |
--------------------------------------------------------------------------------
/.github/workflows/msbuild.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub.
2 | # They are provided by a third-party and are governed by
3 | # separate terms of service, privacy policy, and support
4 | # documentation.
5 |
6 | name: WinSpy
7 |
8 | on:
9 | push:
10 | branches: [ "master" ]
11 | pull_request:
12 | branches: [ "master" ]
13 |
14 | env:
15 | # Path to the solution file relative to the root of the project.
16 | SOLUTION_FILE_PATH: .
17 |
18 | # Configuration type to build.
19 | # You can convert this to a build matrix if you need coverage of multiple configuration types.
20 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
21 | BUILD_CONFIGURATION: Release
22 |
23 | WINSPY_GITHUB_COMMIT: ${{ github.sha }}
24 |
25 | permissions:
26 | contents: read
27 |
28 | jobs:
29 | build:
30 | #strategy:
31 | # matrix:
32 | # configuration: [Release]
33 | # platform: [x64, x86]
34 |
35 | runs-on: windows-latest
36 |
37 | steps:
38 | - uses: actions/checkout@v4
39 |
40 | - name: Add MSBuild to PATH
41 | uses: microsoft/setup-msbuild@v2
42 |
43 | - name: Build Win32
44 | working-directory: ${{env.GITHUB_WORKSPACE}}
45 | # Add additional options to the MSBuild command line here (like platform or verbosity level).
46 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
47 | run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} /p:Platform="Win32" ${{ env.SOLUTION_FILE_PATH }}
48 |
49 | - name: Build x64
50 | working-directory: ${{env.GITHUB_WORKSPACE}}
51 | # Add additional options to the MSBuild command line here (like platform or verbosity level).
52 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
53 | run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} /p:Platform="x64" ${{ env.SOLUTION_FILE_PATH }}
54 |
55 | - name: Package
56 | uses: actions/upload-artifact@v4
57 | with:
58 | name: WinSpy
59 | path: "bin\\*\\Release"
60 |
--------------------------------------------------------------------------------
/WinSpy.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winspy", "src\winspy.vcxproj", "{3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CDF0F9D6-B6D5-44CF-981F-2C4613B98191}"
9 | ProjectSection(SolutionItems) = preProject
10 | build\build.bat = build\build.bat
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|ARM = Debug|ARM
17 | Debug|ARM64 = Debug|ARM64
18 | Debug|Win32 = Debug|Win32
19 | Debug|x64 = Debug|x64
20 | Release|ARM = Release|ARM
21 | Release|ARM64 = Release|ARM64
22 | Release|Win32 = Release|Win32
23 | Release|x64 = Release|x64
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|ARM.ActiveCfg = Debug|ARM
27 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|ARM.Build.0 = Debug|ARM
28 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|ARM64.ActiveCfg = Debug|ARM64
29 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|ARM64.Build.0 = Debug|ARM64
30 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|Win32.ActiveCfg = Debug|Win32
31 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|Win32.Build.0 = Debug|Win32
32 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|x64.ActiveCfg = Debug|x64
33 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Debug|x64.Build.0 = Debug|x64
34 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|ARM.ActiveCfg = Release|ARM
35 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|ARM.Build.0 = Release|ARM
36 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|ARM64.ActiveCfg = Release|ARM64
37 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|ARM64.Build.0 = Release|ARM64
38 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|Win32.ActiveCfg = Release|Win32
39 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|Win32.Build.0 = Release|Win32
40 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|x64.ActiveCfg = Release|x64
41 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}.Release|x64.Build.0 = Release|x64
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | EndGlobal
47 |
--------------------------------------------------------------------------------
/src/FindToolTrans.c:
--------------------------------------------------------------------------------
1 | //
2 | // FindToolTrans.c
3 | //
4 | // www.catch22.net
5 | //
6 | // Copyright (C) 2012 James Brown
7 | // Please refer to the file LICENCE.TXT for copying permission
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | #include "Utils.h"
13 | #include "resource.h"
14 |
15 |
16 | #define WC_TRANSWINDOW TEXT("TransparentWindow")
17 |
18 | HBITMAP MakeDockPanelBitmap(SIZE outputSize)
19 | {
20 | static HBITMAP hbmBox;
21 |
22 | if (hbmBox == 0)
23 | {
24 | hbmBox = LoadPNGImage(IDB_SELBOX, NULL);
25 | }
26 |
27 | RECT edges = { 2, 2, 2, 2 };
28 |
29 | return ExpandNineGridImage(outputSize, hbmBox, edges);
30 | }
31 |
32 |
33 | //
34 | // Very simple window-procedure for the transparent window.
35 | //
36 | LRESULT CALLBACK TransparentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
37 | {
38 | switch (msg)
39 | {
40 | case WM_NCHITTEST:
41 | return HTTRANSPARENT;
42 | }
43 |
44 | return DefWindowProc(hwnd, msg, wParam, lParam);
45 | }
46 |
47 | //
48 | // Creates a transparent overlay window of the same size and position of
49 | // an existing window.
50 | //
51 | HWND CreateOverlayWindow(HWND hwndToCover)
52 | {
53 | HWND hwnd;
54 | HBITMAP hbmp;
55 | RECT rc;
56 | SIZE size;
57 |
58 | // Initialize window class on first use.
59 | static BOOL fInitializedWindowClass = FALSE;
60 |
61 | if (!fInitializedWindowClass)
62 | {
63 | WNDCLASSEX wc = { sizeof(wc) };
64 |
65 | wc.style = 0;
66 | wc.lpszClassName = WC_TRANSWINDOW;
67 | wc.lpfnWndProc = TransparentWndProc;
68 |
69 | RegisterClassEx(&wc);
70 |
71 | fInitializedWindowClass = TRUE;
72 | }
73 |
74 | GetWindowRect(hwndToCover, &rc);
75 |
76 | hwnd = CreateWindowEx(
77 | WS_EX_TOOLWINDOW | WS_EX_LAYERED,
78 | WC_TRANSWINDOW,
79 | 0,
80 | WS_POPUP,
81 | rc.left, rc.top,
82 | GetRectWidth(&rc),
83 | GetRectHeight(&rc),
84 | 0, 0, 0,
85 | NULL);
86 |
87 | size.cx = GetRectWidth(&rc);
88 | size.cy = GetRectHeight(&rc);
89 | hbmp = MakeDockPanelBitmap(size);
90 |
91 | UpdateLayeredWindowContent(hwnd, rc, hbmp, 220);
92 |
93 | DeleteObject(hbmp);
94 |
95 | SetWindowPos(
96 | hwnd,
97 | HWND_TOPMOST,
98 | 0, 0, 0, 0,
99 | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
100 |
101 | return hwnd;
102 | }
103 |
--------------------------------------------------------------------------------
/src/TabCtrlUtils.c:
--------------------------------------------------------------------------------
1 | //
2 | // TabCtrlUtils.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Basic support to remove the flicker from a TAB control
8 | // when it gets resized. This is achieved by subclassing the
9 | // tab control and handling the WM_ERASEBKGND message, so
10 | // that only the necessary parts of the window are actually
11 | // painted. Needs updating if you use in an XP-themed APP.
12 | //
13 |
14 | #include "WinSpy.h"
15 |
16 | //remove flicker from tab control when it is resized
17 | static LRESULT CALLBACK NoFlickerTabProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
18 | {
19 | RECT rect;
20 | HDC hdc;
21 | int n;
22 | int width;
23 |
24 | //int bx, by;
25 |
26 | WNDPROC OldTabProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
27 |
28 | switch (msg)
29 | {
30 | case WM_NCDESTROY:
31 | //do any de-init here.
32 | break;
33 |
34 | case WM_ERASEBKGND:
35 | hdc = (HDC)wParam;
36 |
37 | GetWindowRect(hwnd, &rect);
38 | OffsetRect(&rect, -rect.left, -rect.top);
39 | rect.top++;
40 | width = rect.right;
41 |
42 | //find work area of tab control
43 | TabCtrl_AdjustRect(hwnd, FALSE, (LPARAM)&rect);
44 |
45 | //bx = GetSystemMetrics(SM_CXEDGE);
46 | //by = GetSystemMetrics(SM_CYEDGE);
47 |
48 | //only redraw the area in-between the work area and
49 | //the 3d-look border around the edge.
50 | InflateRect(&rect, 1, 1);
51 | FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNFACE));
52 |
53 | InflateRect(&rect, 1, 1);
54 | FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNFACE));
55 |
56 | // Get coords of last TAB.
57 | n = TabCtrl_GetItemCount(hwnd);
58 | TabCtrl_GetItemRect(hwnd, n - 1, &rect);
59 |
60 | // Now fill the long horz rectangle to the right of the tab.
61 | rect.left = rect.right + 2;
62 | rect.right = width;
63 | rect.top = 0;
64 | FillRect(hdc, &rect, GetSysColorBrush(COLOR_BTNFACE));
65 |
66 | //prevent erasure of window
67 | return 1;
68 | }
69 |
70 | return CallWindowProc(OldTabProc, hwnd, msg, wParam, lParam);
71 | }
72 |
73 | BOOL RemoveTabCtrlFlicker(HWND hwndTab)
74 | {
75 | //Subclass the tab control
76 | WNDPROC oldproc = (WNDPROC)SetWindowLongPtr(hwndTab, GWLP_WNDPROC, (LONG_PTR)NoFlickerTabProc);
77 |
78 | //Store the old window procedure
79 | SetWindowLongPtr(hwndTab, GWLP_USERDATA, (LONG_PTR)oldproc);
80 |
81 | return TRUE;
82 | }
83 |
--------------------------------------------------------------------------------
/src/DisplayScrollInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayScrollInfo.c
3 | // Copyright (c) 2002 by J Brown
4 | // Freeware
5 | //
6 | // Fill the scrollbar-tab-pane with scrollbar info for the
7 | // specified window
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | #include "resource.h"
13 | #include "Utils.h"
14 |
15 | void SetInfo(HWND hwndDlg, HWND hwnd, BOOL fValid, BOOL fVert, PCWSTR ach, DWORD dwStyle)
16 | {
17 | SCROLLINFO si;
18 | DWORD bartype = fVert ? SB_VERT : SB_HORZ;
19 | int idc_state = fVert ? IDC_VSTATE : IDC_HSTATE;
20 |
21 | if (fValid)
22 | {
23 | si.cbSize = sizeof(SCROLLINFO);
24 | si.fMask = SIF_ALL;
25 |
26 | if (lstrcmpi(ach, L"ScrollBar") == 0)
27 | {
28 | static_assert(SBS_HORZ == SB_HORZ && SBS_VERT == SB_VERT, "");
29 | if ((dwStyle & SBS_DIR_MASK) == bartype)
30 | bartype = SB_CTL;
31 |
32 | SetDlgItemTextEx(hwndDlg, idc_state, L"Visible");
33 | }
34 | else
35 | {
36 | SetDlgItemTextEx(hwndDlg, idc_state, dwStyle & ((fVert ? WS_VSCROLL : WS_HSCROLL)) ? L"Visible" : L"Disabled");
37 | }
38 | }
39 |
40 | if (fValid && GetScrollInfo(hwnd, bartype, &si))
41 | {
42 | SetDlgItemInt(hwndDlg, fVert ? IDC_VMIN : IDC_HMIN, si.nMin, TRUE);
43 | SetDlgItemInt(hwndDlg, fVert ? IDC_VMAX : IDC_HMAX, si.nMax, TRUE);
44 | SetDlgItemInt(hwndDlg, fVert ? IDC_VPOS : IDC_HPOS, si.nPos, TRUE);
45 | SetDlgItemInt(hwndDlg, fVert ? IDC_VPAGE : IDC_HPAGE, si.nPage, TRUE);
46 |
47 | if (bartype != SB_CTL)
48 | {
49 | SetDlgItemTextEx(hwndDlg, idc_state, dwStyle & ((fVert ? WS_VSCROLL : WS_HSCROLL)) ? L"Visible" : L"Hidden");
50 | }
51 | }
52 | else
53 | {
54 | SetDlgItemTextEx(hwndDlg, fVert ? IDC_VMIN : IDC_HMIN, fValid ? L"" : ach);
55 | SetDlgItemTextEx(hwndDlg, fVert ? IDC_VMAX : IDC_HMAX, fValid ? L"" : ach);
56 | SetDlgItemTextEx(hwndDlg, fVert ? IDC_VPOS : IDC_HPOS, fValid ? L"" : ach);
57 | SetDlgItemTextEx(hwndDlg, fVert ? IDC_VPAGE : IDC_HPAGE, fValid ? L"" : ach);
58 | SetDlgItemTextEx(hwndDlg, idc_state, fValid ? L"Disabled" : ach);
59 | }
60 | }
61 |
62 | void UpdateScrollbarInfo(HWND hwnd)
63 | {
64 | DWORD dwStyle = 0;
65 | WCHAR ach[256];
66 | HWND hwndDlg = WinSpyTab[PROPERTY_TAB].hwnd;
67 |
68 | *ach = 0;
69 |
70 | BOOL fValid = hwnd != NULL;
71 | if (hwnd && !IsWindow(hwnd))
72 | {
73 | fValid = FALSE;
74 | wcscpy_s(ach, ARRAYSIZE(ach), szInvalidWindow);
75 | }
76 |
77 | if (fValid)
78 | {
79 | GetClassName(hwnd, ach, ARRAYSIZE(ach));
80 |
81 | dwStyle = GetWindowLong(hwnd, GWL_STYLE);
82 | }
83 |
84 | SetInfo(hwndDlg, hwnd, fValid, FALSE, ach, dwStyle);
85 | SetInfo(hwndDlg, hwnd, fValid, TRUE, ach, dwStyle);
86 | }
87 |
--------------------------------------------------------------------------------
/src/RegHelper.c:
--------------------------------------------------------------------------------
1 | //
2 | // RegHelper.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Implements registry helper functions
8 | //
9 |
10 | #include "WinSpy.h"
11 | #include "RegHelper.h"
12 |
13 |
14 | LONG GetSettingBinary(HKEY hkey, PCWSTR szKeyName, void *buf, ULONG nNumBytes)
15 | {
16 | DWORD type = REG_BINARY;
17 |
18 | if (ERROR_SUCCESS == RegQueryValueEx(hkey, szKeyName, 0, &type, (BYTE *)buf, &nNumBytes))
19 | {
20 | if (type != REG_BINARY)
21 | return 0;
22 |
23 | return nNumBytes;
24 | }
25 | else
26 | {
27 | return 0;
28 | }
29 | }
30 |
31 | LONG GetSettingInt(HKEY hkey, PCWSTR szKeyName, LONG nDefault)
32 | {
33 | DWORD type;
34 | LONG value;
35 | ULONG len = sizeof(value);
36 |
37 | if (ERROR_SUCCESS == RegQueryValueEx(hkey, szKeyName, 0, &type, (BYTE *)&value, &len))
38 | {
39 | if (type != REG_DWORD)
40 | return nDefault;
41 |
42 | return value;
43 | }
44 | else
45 | {
46 | return nDefault;
47 | }
48 | }
49 |
50 | BOOL GetSettingBool(HKEY hkey, PCWSTR szKeyName, BOOL nDefault)
51 | {
52 | DWORD type;
53 | BOOL value;
54 | ULONG len = sizeof(value);
55 |
56 | if (ERROR_SUCCESS == RegQueryValueEx(hkey, szKeyName, 0, &type, (BYTE *)&value, &len))
57 | {
58 | if (type != REG_DWORD)
59 | return nDefault;
60 |
61 | return value != 0;
62 | }
63 | else
64 | {
65 | return nDefault;
66 | }
67 | }
68 |
69 | LONG GetSettingStr(HKEY hkey, PCWSTR szKeyName, PCWSTR szDefault, PWSTR szReturnStr, DWORD nSize)
70 | {
71 | DWORD type = REG_SZ;
72 | PCWSTR bigbuf[256];
73 | ULONG len = sizeof(bigbuf);
74 |
75 | if (ERROR_SUCCESS == RegQueryValueEx(hkey, szKeyName, 0, &type, (BYTE *)bigbuf, &len))
76 | {
77 | if (type != REG_SZ)
78 | return 0;
79 |
80 | memcpy(szReturnStr, bigbuf, len + sizeof(WCHAR));
81 | return len;
82 | }
83 | else
84 | {
85 | len = min(nSize, (DWORD)wcslen(szDefault) * sizeof(WCHAR));
86 | memcpy(szReturnStr, szDefault, len + sizeof(WCHAR));
87 | return len;
88 | }
89 | }
90 |
91 | LONG WriteSettingInt(HKEY hkey, PCWSTR szKeyName, LONG nValue)
92 | {
93 | return RegSetValueEx(hkey, szKeyName, 0, REG_DWORD, (BYTE *)&nValue, sizeof(nValue));
94 | }
95 |
96 | LONG WriteSettingBool(HKEY hkey, PCWSTR szKeyName, BOOL nValue)
97 | {
98 | return RegSetValueEx(hkey, szKeyName, 0, REG_DWORD, (BYTE *)&nValue, sizeof(nValue));
99 | }
100 |
101 | LONG WriteSettingStr(HKEY hkey, PCWSTR szKeyName, PCWSTR szString)
102 | {
103 | return RegSetValueEx(hkey, szKeyName, 0, REG_SZ, (BYTE *)szString, (DWORD)(wcslen(szString) + 1) * sizeof(WCHAR));
104 | }
105 |
106 | LONG WriteSettingBinary(HKEY hkey, PCWSTR szKeyName, void *buf, UINT nNumBytes)
107 | {
108 | return RegSetValueEx(hkey, szKeyName, 0, REG_BINARY, (BYTE *)buf, nNumBytes);
109 | }
110 |
--------------------------------------------------------------------------------
/src/FlashWindow.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // FlashWindow.cpp
3 | //
4 |
5 | #include "WinSpy.h"
6 |
7 | #include "Utils.h"
8 | #include "FindTool.h"
9 | #include "resource.h"
10 |
11 | #define WC_FLASHWINDOW L"FlashWindowClass"
12 | #define FLASH_TIMER_ID 101
13 | #define FLASH_TIMER_FREQUENCY 200
14 | #define FLASH_TIMER_ITERATIONS 5
15 |
16 | LRESULT CALLBACK FlashWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
17 | {
18 | if (msg == WM_TIMER)
19 | {
20 | LONG_PTR iteration = GetWindowLongPtr(hwnd, GWLP_USERDATA);
21 |
22 | if (iteration == FLASH_TIMER_ITERATIONS)
23 | {
24 | DestroyWindow(hwnd);
25 | }
26 | else
27 | {
28 | bool fHide = ((iteration % 2) == 0);
29 |
30 | ShowWindow(hwnd, fHide ? SW_HIDE : SW_SHOWNOACTIVATE);
31 |
32 | SetWindowLongPtr(hwnd, GWLP_USERDATA, iteration + 1);
33 |
34 | SetTimer(hwnd, FLASH_TIMER_ID, FLASH_TIMER_FREQUENCY, NULL);
35 | }
36 | }
37 |
38 | return DefWindowProc(hwnd, msg, wParam, lParam);
39 | }
40 |
41 | HBITMAP MakeFlashWindowBitmap(RECT rc)
42 | {
43 | static HBITMAP hbmBox;
44 |
45 | if (hbmBox == 0)
46 | {
47 | hbmBox = LoadPNGImage(IDB_SELBOX, NULL);
48 | }
49 |
50 | RECT edges = { 2, 2, 2, 2 };
51 | SIZE size;
52 |
53 | size.cx = GetRectWidth(&rc);
54 | size.cy = GetRectHeight(&rc);
55 |
56 | return ExpandNineGridImage(size, hbmBox, edges);
57 | }
58 |
59 | HWND CreateFlashWindow(HWND hwndToCover)
60 | {
61 | static BOOL fInitializedWindowClass = FALSE;
62 |
63 | RECT rc;
64 | HWND hwnd;
65 |
66 | if (!fInitializedWindowClass)
67 | {
68 | WNDCLASSEX wc = { sizeof(wc) };
69 |
70 | wc.style = 0;
71 | wc.lpszClassName = WC_FLASHWINDOW;
72 | wc.lpfnWndProc = FlashWndProc;
73 |
74 | RegisterClassEx(&wc);
75 |
76 | fInitializedWindowClass = TRUE;
77 | }
78 |
79 | GetWindowRect(hwndToCover, &rc);
80 |
81 | hwnd = CreateWindowEx(
82 | WS_EX_TOOLWINDOW | WS_EX_LAYERED,
83 | WC_FLASHWINDOW,
84 | 0,
85 | WS_POPUP,
86 | rc.left, rc.top,
87 | rc.right - rc.left,
88 | rc.bottom - rc.top,
89 | 0, 0, 0,
90 | NULL);
91 |
92 | if (hwnd)
93 | {
94 | HBITMAP hbmp = MakeFlashWindowBitmap(rc);
95 | UpdateLayeredWindowContent(hwnd, rc, hbmp, 220);
96 | DeleteObject(hbmp);
97 |
98 | SetWindowPos(
99 | hwnd,
100 | HWND_TOPMOST,
101 | 0, 0, 0, 0,
102 | SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW);
103 |
104 | SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
105 |
106 | SetTimer(hwnd, FLASH_TIMER_ID, FLASH_TIMER_FREQUENCY, NULL);
107 | }
108 |
109 | return hwnd;
110 | }
111 |
112 | void FlashWindowBorder(HWND hwnd)
113 | {
114 | HWND hwndFlash = CreateFlashWindow(hwnd);
115 |
116 | if (!hwndFlash)
117 | {
118 | MessageBeep(NULL);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/DisplayWindowInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayWindowInfo.c
3 | // Copyright (c) 2002 by J Brown
4 | // Freeware
5 | //
6 | // Fill the window-tab-pane with list of child+siblings
7 | //
8 |
9 | #include "WinSpy.h"
10 |
11 | #include "resource.h"
12 | #include "Utils.h"
13 |
14 | static BOOL CALLBACK ChildWindowProc(HWND hwnd, LPARAM lParam)
15 | {
16 | WCHAR ach[256];
17 | WCHAR cname[256];
18 | WCHAR wname[256];
19 | LVITEM lvitem;
20 |
21 | //only display 1st generation (1-deep) children -
22 | //(don't display child windows of child windows)
23 | if (GetRealParent(hwnd) == g_hCurWnd)
24 | {
25 | GetClassName(hwnd, cname, ARRAYSIZE(cname));
26 | GetWindowText(hwnd, wname, ARRAYSIZE(wname));
27 | swprintf_s(ach, ARRAYSIZE(ach), L"%08X", (UINT)(UINT_PTR)hwnd);
28 |
29 | lvitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
30 | lvitem.iSubItem = 0;
31 | lvitem.pszText = ach;
32 | lvitem.iItem = 0;
33 | lvitem.state = 0;
34 | lvitem.stateMask = 0;
35 | lvitem.iImage = 0;
36 |
37 | ListView_InsertItem((HWND)lParam, &lvitem);
38 | ListView_SetItemText((HWND)lParam, 0, 1, cname);
39 | ListView_SetItemText((HWND)lParam, 0, 2, wname);
40 | }
41 | return TRUE;
42 | }
43 |
44 | static BOOL CALLBACK SiblingWindowProc(HWND hwnd, LPARAM lParam)
45 | {
46 | WCHAR ach[256];
47 | WCHAR cname[256];
48 | WCHAR wname[256];
49 | LVITEM lvitem;
50 |
51 | //sibling windows must share the same parent
52 | if (g_hCurWnd != hwnd && GetRealParent(hwnd) == GetRealParent(g_hCurWnd))
53 | {
54 | GetClassName(hwnd, cname, ARRAYSIZE(cname));
55 | GetWindowText(hwnd, wname, ARRAYSIZE(wname));
56 | swprintf_s(ach, ARRAYSIZE(ach), L"%08X", (UINT)(UINT_PTR)hwnd);
57 |
58 | lvitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
59 | lvitem.iSubItem = 0;
60 | lvitem.pszText = ach;
61 | lvitem.iItem = 0;
62 | lvitem.state = 0;
63 | lvitem.stateMask = 0;
64 | lvitem.iImage = 0;
65 |
66 | ListView_InsertItem((HWND)lParam, &lvitem);
67 | ListView_SetItemText((HWND)lParam, 0, 1, cname);
68 | ListView_SetItemText((HWND)lParam, 0, 2, wname);
69 | }
70 |
71 | return TRUE;
72 | }
73 |
74 | //
75 | // Get a list of all Child + Siblings for the specified window -
76 | // Update the Windows tab accordingly
77 | //
78 | void UpdateWindowTab(HWND hwnd)
79 | {
80 | HWND hParentWnd = NULL;
81 | WCHAR ach[10];
82 |
83 | HWND hwndList1 = GetDlgItem(WinSpyTab[WINDOW_TAB].hwnd, IDC_LIST1);
84 | HWND hwndList2 = GetDlgItem(WinSpyTab[WINDOW_TAB].hwnd, IDC_LIST2);
85 | HWND hwndLink;
86 |
87 | ListView_DeleteAllItems(hwndList1);
88 | ListView_DeleteAllItems(hwndList2);
89 |
90 |
91 | // Get all children of the window
92 | if (hwnd)
93 | {
94 | EnumChildWindows(hwnd, ChildWindowProc, (LPARAM)hwndList1);
95 |
96 | // Get children of its PARENT (i.e, its siblings!)
97 | hParentWnd = GetRealParent(hwnd);
98 | if (hParentWnd)
99 | EnumChildWindows(hParentWnd, SiblingWindowProc, (LPARAM)hwndList2);
100 | }
101 |
102 | // Set the Parent hyperlink
103 | hwndLink = GetDlgItem(WinSpyTab[WINDOW_TAB].hwnd, IDC_PARENT);
104 |
105 | if (hParentWnd)
106 | {
107 | swprintf_s(ach, ARRAYSIZE(ach), L"%08X", (UINT)(UINT_PTR)hParentWnd);
108 | }
109 | else
110 | {
111 | *ach = 0;
112 | }
113 |
114 | SetWindowText(hwndLink, ach);
115 | EnableWindow(hwndLink, (*ach != 0));
116 |
117 | // Set the Owner hyperlink
118 | HWND hwndOwner = hwnd ? GetWindow(hwnd, GW_OWNER) : NULL;
119 |
120 | hwndLink = GetDlgItem(WinSpyTab[WINDOW_TAB].hwnd, IDC_OWNER);
121 |
122 | if (hwndOwner)
123 | {
124 | swprintf_s(ach, ARRAYSIZE(ach), L"%08X", (UINT)(UINT_PTR)hwndOwner);
125 | }
126 | else
127 | {
128 | *ach = 0;
129 | }
130 |
131 | SetWindowText(hwndLink, ach);
132 | EnableWindow(hwndLink, (*ach != 0));
133 | }
134 |
--------------------------------------------------------------------------------
/src/PropertyEdit.c:
--------------------------------------------------------------------------------
1 | //
2 | // PropertyEdit.c
3 | //
4 | // Copyright (c)
5 | // Freeware
6 | //
7 | // Implements the Property Editor dialog box
8 | //
9 |
10 | #include "WinSpy.h"
11 | #include "resource.h"
12 | #include "Utils.h"
13 |
14 | typedef struct
15 | {
16 | HWND hwndTarget; // what window are we looking at??
17 | BOOL bAddNew;
18 |
19 | WCHAR szString[256];
20 | ATOM aAtom;
21 |
22 | } PropertyEditState;
23 |
24 | INT_PTR CALLBACK PropertyEditProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
25 | {
26 | static PropertyEditState *state;
27 |
28 | HWND hwndList;
29 |
30 | HANDLE hHandle;
31 | WCHAR szText[32];
32 |
33 | switch (iMsg)
34 | {
35 | case WM_INITDIALOG:
36 |
37 | // Passed through in call to DialogBoxParam
38 | state = (PropertyEditState *)lParam;
39 |
40 | hwndList = GetDlgItem(hwnd, IDC_LIST1);
41 |
42 | if (state->bAddNew)
43 | {
44 | CheckRadioButton(hwnd, IDC_RADIO_NAME, IDC_RADIO_ATOM, IDC_RADIO_NAME);
45 |
46 | EnableWindow(GetDlgItem(hwnd, IDC_APPLY), FALSE);
47 | }
48 | else
49 | {
50 | if (state->aAtom)
51 | {
52 | CheckRadioButton(hwnd, IDC_RADIO_NAME, IDC_RADIO_ATOM, IDC_RADIO_ATOM);
53 | EnableWindow(GetDlgItem(hwnd, IDC_RADIO_NAME), FALSE);
54 |
55 | swprintf_s(szText, ARRAYSIZE(szText), L"%04hX", state->aAtom);
56 | SetDlgItemText(hwnd, IDC_EDIT_NAME, szText);
57 |
58 | hHandle = GetProp(state->hwndTarget, MAKEINTATOM(state->aAtom));
59 | }
60 | else
61 | {
62 | CheckRadioButton(hwnd, IDC_RADIO_NAME, IDC_RADIO_ATOM, IDC_RADIO_NAME);
63 | EnableWindow(GetDlgItem(hwnd, IDC_RADIO_ATOM), FALSE);
64 |
65 | SetDlgItemText(hwnd, IDC_EDIT_NAME, state->szString);
66 |
67 | hHandle = GetProp(state->hwndTarget, state->szString);
68 | }
69 |
70 | SendDlgItemMessage(hwnd, IDC_EDIT_NAME, EM_SETREADONLY, TRUE, 0);
71 |
72 | swprintf_s(szText, ARRAYSIZE(szText), L"%p", hHandle);
73 | SetDlgItemText(hwnd, IDC_EDIT_HANDLE, szText);
74 | }
75 |
76 | return TRUE;
77 |
78 | case WM_CLOSE:
79 | EndDialog(hwnd, 0);
80 | return TRUE;
81 |
82 | case WM_COMMAND:
83 | switch (LOWORD(wParam))
84 | {
85 | case IDC_EDIT_NAME:
86 | case IDC_EDIT_HANDLE:
87 |
88 | if (HIWORD(wParam) == EN_CHANGE)
89 | {
90 | EnableWindow(
91 | GetDlgItem(hwnd, IDC_APPLY),
92 | SendDlgItemMessage(hwnd, IDC_EDIT_NAME, WM_GETTEXTLENGTH, 0, 0) > 0 &&
93 | SendDlgItemMessage(hwnd, IDC_EDIT_HANDLE, WM_GETTEXTLENGTH, 0, 0) > 0
94 | );
95 | }
96 |
97 | return TRUE;
98 |
99 | case IDC_APPLY:
100 |
101 | hHandle = (HANDLE)GetDlgItemBaseInt(hwnd, IDC_EDIT_HANDLE, 16);
102 |
103 | if (IsDlgButtonChecked(hwnd, IDC_RADIO_ATOM))
104 | {
105 | state->aAtom = (ATOM)GetDlgItemBaseInt(hwnd, IDC_EDIT_NAME, 16);
106 | SetProp(state->hwndTarget, MAKEINTATOM(state->aAtom), hHandle);
107 | }
108 | else
109 | {
110 | GetDlgItemText(hwnd, IDC_EDIT_NAME, state->szString, 256);
111 | SetProp(state->hwndTarget, state->szString, hHandle);
112 | }
113 |
114 | return TRUE;
115 |
116 | case IDCANCEL:
117 | EndDialog(hwnd, 0);
118 | return TRUE;
119 |
120 | }
121 | return FALSE;
122 | }
123 | return FALSE;
124 | }
125 |
126 |
127 | void ShowWindowPropertyEditor(HWND hwndParent, HWND hwndTarget, BOOL bAddNew)
128 | {
129 | HWND hwndList;
130 | LVITEM lvitem;
131 | PropertyEditState state;
132 |
133 | state.hwndTarget = hwndTarget;
134 | state.bAddNew = bAddNew;
135 |
136 | if (!bAddNew)
137 | {
138 | hwndList = GetDlgItem(hwndParent, IDC_LIST1);
139 |
140 | if (ListView_GetSelectedCount(hwndList) != 1)
141 | return;
142 |
143 | lvitem.mask = LVIF_TEXT | LVIF_PARAM;
144 | lvitem.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
145 | lvitem.iSubItem = 0;
146 | lvitem.pszText = state.szString;
147 | lvitem.cchTextMax = 256;
148 |
149 | ListView_GetItem(hwndList, &lvitem);
150 |
151 | state.aAtom = (ATOM)lvitem.lParam;
152 | }
153 |
154 | DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_PROPERTY_EDIT), hwndParent, PropertyEditProc, (LPARAM)&state);
155 |
156 | // Update the main display
157 | UpdatePropertyTab(hwndTarget);
158 | }
159 |
--------------------------------------------------------------------------------
/src/StaticCtrl.c:
--------------------------------------------------------------------------------
1 | //
2 | // StaticCtrl.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // void MakeHyperlink(HWND hwnd, UINT staticid, COLORREF crLink)
8 | //
9 | // Creates a very basic hyperlink control from a standard
10 | // static label.
11 | //
12 | // Use the standard control notifications (WM_COMMAND) to
13 | // detect mouse clicks.
14 | //
15 |
16 | #include "WinSpy.h"
17 |
18 | // Keep track of how many URL controls we have.
19 | static LONG lRefCount = 0;
20 |
21 | //
22 | static HFONT hfUnderlined;
23 | static HCURSOR hCursor;
24 |
25 | typedef struct
26 | {
27 | WNDPROC oldproc;
28 | COLORREF crLink;
29 | COLORREF crVisited;
30 | } URLCtrl;
31 |
32 | void FreeHyperlink(HWND hwnd)
33 | {
34 | void *mem = (void *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
35 |
36 | HeapFree(GetProcessHeap(), 0, mem);
37 |
38 | // Clean up font and cursor resources when the last
39 | // hyperlink is destroyed
40 | if (InterlockedDecrement(&lRefCount) == 0)
41 | {
42 | DeleteObject(hfUnderlined);
43 | }
44 |
45 | SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
46 | }
47 |
48 | static LRESULT CALLBACK URLCtrlProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
49 | {
50 | PAINTSTRUCT ps;
51 | HDC hdc;
52 |
53 | URLCtrl *url = (URLCtrl *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
54 | WNDPROC oldproc = url->oldproc;
55 |
56 | RECT rect;
57 | SIZE sz;
58 | int nTextLen;
59 | HANDLE hOld;
60 |
61 | static WCHAR szWinText[MAX_PATH];
62 |
63 | switch (iMsg)
64 | {
65 | case WM_NCDESTROY:
66 | FreeHyperlink(hwnd);
67 | break;
68 |
69 | case WM_PAINT:
70 |
71 | GetClientRect(hwnd, &rect);
72 |
73 | hdc = BeginPaint(hwnd, &ps);
74 |
75 | // Set the font colors
76 | SetTextColor(hdc, url->crLink);
77 |
78 | hOld = SelectObject(hdc, hfUnderlined);
79 |
80 | // find text to draw
81 | nTextLen = GetWindowText(hwnd, szWinText, ARRAYSIZE(szWinText));
82 |
83 | // find width / height of text
84 | GetTextExtentPoint32(hdc, szWinText, nTextLen, &sz);
85 |
86 | // Draw text + fill background at the same time
87 | ExtTextOut(hdc, 0, (rect.bottom - sz.cy), 0/*ETO_OPAQUE*/, &rect, szWinText, nTextLen, 0);
88 |
89 | SelectObject(hdc, hOld);
90 |
91 | EndPaint(hwnd, &ps);
92 |
93 | return 0;
94 |
95 | case WM_SETTEXT:
96 | CallWindowProc(oldproc, hwnd, iMsg, wParam, lParam);
97 | InvalidateRect(hwnd, 0, 0);
98 | return 0;
99 |
100 | case WM_SETCURSOR:
101 | SetCursor(hCursor);
102 | return TRUE;
103 | }
104 |
105 | return CallWindowProc(oldproc, hwnd, iMsg, wParam, lParam);
106 | }
107 |
108 | void MakeHyperlink(HWND hwnd, UINT staticid, COLORREF crLink)
109 | {
110 | URLCtrl *url;
111 | HWND hwndCtrl = GetDlgItem(hwnd, staticid);
112 |
113 | // If already a hyperlink
114 | if ((UINT_PTR)GetWindowLongPtr(hwndCtrl, GWLP_WNDPROC) == (UINT_PTR)URLCtrlProc)
115 | return;
116 |
117 | url = (URLCtrl *)HeapAlloc(GetProcessHeap(), 0, sizeof(URLCtrl));
118 |
119 | // Create font and cursor resources if this is
120 | // the first control being created
121 | if (InterlockedIncrement(&lRefCount) == 1)
122 | {
123 | LOGFONT lf;
124 | HFONT hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
125 |
126 | GetObject(hf, sizeof lf, &lf);
127 | lf.lfUnderline = TRUE;
128 | hfUnderlined = CreateFontIndirect(&lf);
129 |
130 | hCursor = LoadCursor(NULL, IDC_HAND);
131 | }
132 |
133 | //turn on notify style
134 | SetWindowLong(hwndCtrl, GWL_STYLE, GetWindowLong(hwndCtrl, GWL_STYLE) | SS_NOTIFY);
135 | SetWindowLong(hwndCtrl, GWL_EXSTYLE, GetWindowLong(hwndCtrl, GWL_EXSTYLE) | WS_EX_TRANSPARENT);
136 |
137 |
138 | // setup colors
139 | if (crLink != -1) url->crLink = crLink;
140 | else url->crLink = RGB(0, 0, 255);
141 | url->crVisited = RGB(128, 0, 128);
142 |
143 | SendMessage(hwndCtrl, WM_SETFONT, (WPARAM)hfUnderlined, 0);
144 |
145 | // subclass
146 | url->oldproc = (WNDPROC)SetWindowLongPtr(hwndCtrl, GWLP_WNDPROC, (LONG_PTR)URLCtrlProc);
147 | SetWindowLongPtr(hwndCtrl, GWLP_USERDATA, (LONG_PTR)url);
148 |
149 | return;
150 | }
151 |
152 | void RemoveHyperlink(HWND hwnd, UINT staticid)
153 | {
154 | HWND hwndCtrl = GetDlgItem(hwnd, staticid);
155 |
156 | URLCtrl *url = (URLCtrl *)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA);
157 |
158 | // if this isn't a hyperlink control...
159 | if (url == 0 || (UINT_PTR)GetWindowLongPtr(hwndCtrl, GWLP_WNDPROC) != (UINT_PTR)URLCtrlProc)
160 | return;
161 |
162 | // Restore the window procedure
163 | SetWindowLongPtr(hwndCtrl, GWLP_WNDPROC, (LONG_PTR)url->oldproc);
164 |
165 | FreeHyperlink(hwndCtrl);
166 | }
167 |
--------------------------------------------------------------------------------
/src/InjectThread.c:
--------------------------------------------------------------------------------
1 | //
2 | // InjectThread.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // InjectThread uses the CreateRemoteThread API call
8 | // (under NT/2000/XP) to inject a piece of binary code
9 | // into the process which owns the specified window.
10 | //
11 | //
12 |
13 | #include "WinSpy.h"
14 |
15 | #include "InjectThread.h"
16 |
17 | #define INJECT_ACCESS (PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE)
18 |
19 | typedef PVOID(WINAPI * VA_EX_PROC)(HANDLE, PVOID, SIZE_T, DWORD, DWORD);
20 | typedef PVOID(WINAPI * VF_EX_PROC)(HANDLE, PVOID, SIZE_T, DWORD);
21 |
22 | //
23 | // Inject a thread into the process which owns the specified window.
24 | //
25 | // lpCode - address of function to inject. (See CreateRemoteThread)
26 | // cbCodeSize - size (in bytes) of the function
27 | //
28 | // lpData - address of a user-defined structure to be passed to the injected thread
29 | // cbDataSize - size (in bytes) of the structure
30 | // cbInput - size (in bytes) of the input part of the data (the rest is output)
31 | //
32 | // The user-defined structure is also injected into the target process' address space.
33 | // When the thread terminates, the structure is read back from the process.
34 | //
35 | DWORD InjectRemoteThread(HWND hwnd, LPTHREAD_START_ROUTINE lpCode, DWORD_PTR cbCodeSize, PVOID lpData, DWORD cbDataSize, DWORD cbInput)
36 | {
37 | DWORD dwProcessId; //id of remote process
38 | DWORD dwThreadId; //id of the thread in remote process
39 | HANDLE hProcess; //handle to the remote process
40 |
41 | HANDLE hRemoteThread; //handle to the injected thread
42 |
43 | SIZE_T dwWritten; // Number of bytes written to the remote process
44 | SIZE_T dwRead;
45 | DWORD dwExitCode;
46 |
47 | LPTHREAD_START_ROUTINE pRemoteCode;
48 | void *pRemoteData;
49 |
50 | const DWORD_PTR cbCodeSizeAligned = (cbCodeSize + (sizeof(LONG_PTR) - 1)) & ~(sizeof(LONG_PTR) - 1);
51 |
52 | // Return FALSE in case of failure
53 | dwExitCode = FALSE;
54 |
55 | // Find the process ID of the process which created the specified window
56 | dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId);
57 |
58 | // Open the remote process so we can allocate some memory in it
59 | hProcess = OpenProcess(INJECT_ACCESS, FALSE, dwProcessId);
60 | if (hProcess)
61 | {
62 | pRemoteCode = (LPTHREAD_START_ROUTINE)(intptr_t)VirtualAllocEx(hProcess, 0, cbCodeSizeAligned + cbDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
63 | if (pRemoteCode)
64 | {
65 | do
66 | {
67 | // Write a copy of our injection code into the remote process
68 | if (!(WriteProcessMemory(hProcess, (void *)(intptr_t)pRemoteCode, (void *)(intptr_t)lpCode, cbCodeSize, &dwWritten) && dwWritten == cbCodeSize))
69 | break;
70 |
71 | // Write a copy of the data to the remote process. This structure
72 | // MUST start on a 32bit/64bit boundary
73 | pRemoteData = (void *)((BYTE *)(intptr_t)pRemoteCode + cbCodeSizeAligned);
74 |
75 | // Put data in the remote thread's memory block
76 | if (!(WriteProcessMemory(hProcess, pRemoteData, lpData, cbInput, &dwWritten) && dwWritten == cbInput))
77 | break;
78 |
79 | // Create the remote thread!!!
80 | hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
81 | pRemoteCode, pRemoteData, 0, NULL);
82 |
83 | if (hRemoteThread)
84 | {
85 | // Wait for the thread to terminate
86 | if (WaitForSingleObject(hRemoteThread, 7000) != WAIT_OBJECT_0)
87 | {
88 | // Timeout or failure
89 | // Do not call VirtualFreeEx as the code may still run in the future
90 | CloseHandle(hRemoteThread);
91 | CloseHandle(hProcess);
92 |
93 | return FALSE;
94 | }
95 |
96 | // Read the user-structure back again
97 | if (!(ReadProcessMemory(hProcess, (BYTE *)(intptr_t)pRemoteData + cbInput, (BYTE *)(intptr_t)lpData + cbInput, cbDataSize - cbInput, &dwRead) && dwRead == cbDataSize - cbInput))
98 | break;
99 |
100 | GetExitCodeThread(hRemoteThread, &dwExitCode);
101 |
102 | CloseHandle(hRemoteThread);
103 | }
104 | } while (0);
105 |
106 | // Free the memory in the remote process
107 | VirtualFreeEx(hProcess, (void *)(intptr_t)pRemoteCode, 0, MEM_RELEASE);
108 | }
109 |
110 | CloseHandle(hProcess);
111 | }
112 |
113 | return dwExitCode;
114 | }
115 |
--------------------------------------------------------------------------------
/src/WindowFromPointEx.c:
--------------------------------------------------------------------------------
1 | //
2 | // WindowFromPointEx.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Provides a better implementation of WindowFromPoint.
8 | // This function can return any window under the mouse,
9 | // including controls nested inside group-boxes, nested
10 | // dialogs etc.
11 | //
12 |
13 | #include "WinSpy.h"
14 | #include "Utils.h"
15 | #include "WindowFromPointEx.h"
16 |
17 | typedef struct
18 | {
19 | POINT pt;
20 | HWND hwndBest;
21 | BOOL fAllowHidden;
22 | DWORD dwArea;
23 | } ChildSearchData;
24 |
25 | //
26 | // Callback function used with FindBestChild
27 | //
28 | static BOOL CALLBACK FindBestChildProc(HWND hwnd, LPARAM lParam)
29 | {
30 | ChildSearchData *pData = (ChildSearchData *)lParam;
31 | RECT rect;
32 |
33 | GetWindowRect(hwnd, &rect);
34 |
35 | // Is the mouse inside this child window?
36 | if (PtInRect(&rect, pData->pt))
37 | {
38 | // work out area of child window.
39 | // Width and height of any screen rectangle are guaranteed to be <32K each,
40 | // so their product is definitely much smaller than MAXINT
41 | DWORD a = GetRectWidth(&rect) * GetRectHeight(&rect);
42 |
43 | // if this child window is smaller than the
44 | // current "best", then choose this one
45 | if (a < pData->dwArea && (pData->fAllowHidden || IsWindowVisible(hwnd)))
46 | {
47 | pData->dwArea = a;
48 | pData->hwndBest = hwnd;
49 | }
50 | }
51 |
52 | return TRUE;
53 | }
54 |
55 | //
56 | // The problem:
57 | //
58 | // WindowFromPoint API is not very good. It cannot cope
59 | // with odd window arrangements, i.e. a group-box in a dialog
60 | // may contain a few check-boxes. These check-boxes are not
61 | // children of the groupbox, but are at the same "level" in the
62 | // window hierarchy. WindowFromPoint will just return the
63 | // first available window it finds which encompasses the mouse
64 | // (i.e. the group-box), but will NOT be able to detect the contents.
65 | //
66 | // Solution:
67 | //
68 | // We use WindowFromPoint to start us off, and then step back one
69 | // level (i.e. from the parent of what WindowFromPoint returned).
70 | //
71 | // Once we have this window, we enumerate ALL children of this window
72 | // ourselves, and find the one that best fits under the mouse -
73 | // the smallest window that fits, in fact.
74 | //
75 | // I've tested this on a lot of different apps, and it seems
76 | // to work flawlessly - in fact, I haven't found a situation yet
77 | // that this method doesn't work on.....we'll see!
78 | //
79 | // Inputs:
80 | //
81 | // hwndFound - window found with WindowFromPoint
82 | // pt - coordinates of mouse, in screen coords
83 | // (i.e. same coords used with WindowFromPoint)
84 | // fAllowHidden - whether to include hidden windows in the search
85 | //
86 | static HWND FindBestChild(HWND hwndFound, POINT pt, BOOL fAllowHidden)
87 | {
88 | HWND hwnd;
89 | DWORD dwStyle;
90 |
91 | ChildSearchData data;
92 | data.fAllowHidden = fAllowHidden;
93 | data.dwArea = MAXUINT;
94 | data.hwndBest = 0;
95 | data.pt = pt;
96 |
97 | hwnd = GetParent(hwndFound);
98 |
99 | dwStyle = GetWindowLong(hwndFound, GWL_STYLE);
100 |
101 | // The original window might already be a top-level window,
102 | // so we don't want to start at *its* parent
103 | if (hwnd == 0 || (dwStyle & WS_POPUP))
104 | hwnd = hwndFound;
105 |
106 | // Enumerate EVERY child window.
107 | //
108 | // Note to reader:
109 | //
110 | // You can get some real interesting effects if you set
111 | // hwnd = GetDesktopWindow()
112 | // fAllowHidden = TRUE
113 | // ...experiment!!
114 | //
115 | EnumChildWindows(hwnd, FindBestChildProc, (LPARAM)&data);
116 |
117 | if (data.hwndBest == 0)
118 | data.hwndBest = hwnd;
119 |
120 | return data.hwndBest;
121 | }
122 |
123 | //
124 | // Find window under specified point (screen coordinates)
125 | //
126 | HWND WindowFromPointEx(POINT pt, BOOL fTopLevel, BOOL fAllowHidden)
127 | {
128 | HWND hWndPoint;
129 |
130 | //
131 | // First of all find the parent window under the mouse
132 | // We are working in SCREEN coordinates
133 | //
134 | hWndPoint = WindowFromPoint(pt);
135 |
136 | if (hWndPoint == 0)
137 | return 0;
138 |
139 | if (fTopLevel)
140 | {
141 | hWndPoint = GetAncestor(hWndPoint, GA_ROOT);
142 | }
143 | else
144 | {
145 | // WindowFromPoint is not too accurate. There is quite likely
146 | // another window under the mouse.
147 | hWndPoint = FindBestChild(hWndPoint, pt, fAllowHidden);
148 |
149 | //if we don't allow hidden windows, then return the parent
150 | if (!fAllowHidden)
151 | {
152 | while (hWndPoint && !IsWindowVisible(hWndPoint))
153 | hWndPoint = GetRealParent(hWndPoint);
154 | }
155 | }
156 |
157 | return hWndPoint;
158 | }
159 |
--------------------------------------------------------------------------------
/src/FunkyList.c:
--------------------------------------------------------------------------------
1 | //
2 | // WinSpy Finder Tool.
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Nice-looking owner-drawn list (used for style-lists).
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | //
13 | // Called from WM_MEASUREITEM
14 | //
15 | BOOL FunkyList_MeasureItem(HWND hwnd, MEASUREITEMSTRUCT *mis)
16 | {
17 | // For a LBS_OWNERDRAWFIXED listbox, WM_MEASUREITEM is sent just once
18 | // from the WM_CREATE handler of the listbox window. At that point in
19 | // time the owning dialog's font has not yet been set on the listbox, and
20 | // the default itemHeight in the MEASUREITEMSTRUCT will have been derived
21 | // from the default DC font (what you get on a DC that doesn't have any
22 | // explicitly selected font). The default DC metrics are fixed and are
23 | // really only suitable when running at 100% DPI. If running at any DPI
24 | // awareness level other than unaware, and at a DPI scale above 100% then
25 | // that default height will cause the bottom of text to be clipped.
26 | //
27 | // To gracefully handle running at high DPI we need to explicitly measure
28 | // the height of the text here. We can query the font to be used from
29 | // the parent of the listbox (i.e. the owning dialog).
30 |
31 | HWND hwndDialog = GetParent(hwnd);
32 | HFONT hfont = (HFONT)SendMessageA(hwndDialog, WM_GETFONT, 0, 0);
33 |
34 | HDC hdc = GetDC(hwnd);
35 | HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
36 | TEXTMETRIC tm;
37 |
38 | if (GetTextMetrics(hdc, &tm))
39 | {
40 | mis->itemHeight = tm.tmHeight;
41 | }
42 |
43 | SelectObject(hdc, hfontOld);
44 | ReleaseDC(hwnd, hdc);
45 |
46 | return TRUE;
47 | }
48 |
49 | //
50 | // Super owner-drawn list!
51 | //
52 | // All we do is draw the list normally, but with a couple of minor changes:
53 | //
54 | // Each list item will have its user-defined dataitem set to the definition
55 | // of the style it represents.
56 | //
57 | // If this style's value is zero, this means that it is an implicit style, so
58 | // draw the whole line gray.
59 | //
60 | // Also, at the end of every line, right-align the hex-values of each style
61 | //
62 | BOOL FunkyList_DrawItem(HWND hwnd, UINT uCtrlId, DRAWITEMSTRUCT *dis)
63 | {
64 | HWND hwndList = GetDlgItem(hwnd, uCtrlId);
65 | WCHAR szText[MAX_STYLE_NAME_CCH];
66 |
67 | COLORREF crFG = GetTextColor(dis->hDC);
68 | COLORREF crBG = GetBkColor(dis->hDC);
69 |
70 | switch (dis->itemAction)
71 | {
72 | case ODA_FOCUS:
73 | DrawFocusRect(dis->hDC, &dis->rcItem);
74 | break;
75 |
76 | case ODA_SELECT:
77 | case ODA_DRAWENTIRE:
78 |
79 | // get the text string to display, and the item state.
80 | // In general, calling LB_GETTEXT is not safe unless we are sure the text length does not exceed our buffer.
81 | // Therefore, we use a loose equivalent of a static_assert in the definition of the NAMEANDVALUE_ macro to make sure that our style name lengths never exceed MAX_STYLE_NAME_CCH
82 | static_assert(ARRAYSIZE(szText) >= MAX_STYLE_NAME_CCH, "Buffer length is smaller than the maximum possible item text length");
83 | SendMessage(hwndList, LB_GETTEXT, dis->itemID, (LONG_PTR)szText);
84 | StyleLookupEx *pStyle = (StyleLookupEx *)dis->itemData;
85 |
86 | if ((dis->itemState & ODS_SELECTED))
87 | {
88 | SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
89 | SetBkColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHT));
90 | }
91 | else
92 | {
93 | // Make the item greyed-out if the style is zero
94 | if (pStyle && pStyle->value == 0)
95 | SetTextColor(dis->hDC, GetSysColor(COLOR_3DSHADOW));
96 | else
97 | SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT));
98 |
99 | SetBkColor(dis->hDC, GetSysColor(COLOR_WINDOW));
100 | }
101 |
102 | //draw the item text first of all. The ExtTextOut function also
103 | //lets us draw a rectangle under the text, so we use this facility
104 | //to draw the whole line at once.
105 | ExtTextOut(dis->hDC,
106 | dis->rcItem.left + 2,
107 | dis->rcItem.top + 0,
108 | ETO_OPAQUE, &dis->rcItem, szText, (UINT)wcslen(szText), 0);
109 |
110 | if (!pStyle && (szText[0] == '<'))
111 | {
112 | // This is an error message. Do not draw the value on the right.
113 | }
114 | else
115 | {
116 | //Draw the style bytes
117 | if ((dis->itemState & ODS_SELECTED))
118 | SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
119 | else
120 | SetTextColor(dis->hDC, GetSysColor(COLOR_3DSHADOW));
121 |
122 | if (pStyle)
123 | swprintf_s(szText, ARRAYSIZE(szText), L"%08X", pStyle->value); // otherwise, this is the "unrecognized bits" item and its text coincides with its numeric value
124 |
125 | dis->rcItem.right -= 4;
126 |
127 | DrawText(dis->hDC, szText, -1, &dis->rcItem, DT_RIGHT | DT_SINGLELINE | DT_VCENTER);
128 |
129 | dis->rcItem.right += 4;
130 | }
131 |
132 | SetTextColor(dis->hDC, crFG);
133 | SetBkColor(dis->hDC, crBG);
134 |
135 | if (dis->itemState & ODS_FOCUS)
136 | DrawFocusRect(dis->hDC, &dis->rcItem);
137 |
138 | break;
139 | }
140 |
141 |
142 | return TRUE;
143 | }
144 |
--------------------------------------------------------------------------------
/src/LoadPNG.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // LoadPNG.cpp
3 | //
4 | // www.catch22.net
5 | //
6 | // Copyright (C) 2012 James Brown
7 | // Please refer to the file LICENCE.TXT for copying permission
8 | //
9 |
10 | #include "WinSpy.h"
11 | #include
12 |
13 | //#import "wincodec.idl" no_namespace
14 |
15 | // Creates a stream object initialized with the data from an executable resource.
16 | IStream * CreateStreamOnResource(PCWSTR lpName, PCWSTR lpType)
17 | {
18 | IStream * ipStream = NULL;
19 |
20 | // find the resource
21 | HRSRC hRes = FindResource(NULL, lpName, lpType);
22 | DWORD dwResourceSize;
23 | HGLOBAL hglbImage;
24 | PVOID pvSourceResourceData;
25 | HGLOBAL hgblResourceData;
26 | PVOID pvResourceData;
27 |
28 | if (hRes == NULL)
29 | goto Return;
30 |
31 | // load the resource
32 | dwResourceSize = SizeofResource(NULL, hRes);
33 | hglbImage = LoadResource(NULL, hRes);
34 |
35 | if (hglbImage == NULL)
36 | goto Return;
37 |
38 | // lock the resource, getting a pointer to its data
39 | pvSourceResourceData = LockResource(hglbImage);
40 |
41 | if (pvSourceResourceData == NULL)
42 | goto Return;
43 |
44 | // allocate memory to hold the resource data
45 | hgblResourceData = GlobalAlloc(GMEM_MOVEABLE, dwResourceSize);
46 |
47 | if (hgblResourceData == NULL)
48 | goto Return;
49 |
50 | // get a pointer to the allocated memory
51 | pvResourceData = GlobalLock(hgblResourceData);
52 |
53 | if (pvResourceData == NULL)
54 | goto FreeData;
55 |
56 | // copy the data from the resource to the new memory block
57 | CopyMemory(pvResourceData, pvSourceResourceData, dwResourceSize);
58 |
59 | GlobalUnlock(hgblResourceData);
60 |
61 | // create a stream on the HGLOBAL containing the data
62 | // Specify that the HGLOBAL will be automatically free'd on the last Release()
63 | if (SUCCEEDED(CreateStreamOnHGlobal(hgblResourceData, TRUE, &ipStream)))
64 | goto Return;
65 |
66 | FreeData:
67 |
68 | // couldn't create stream; free the memory
69 | GlobalFree(hgblResourceData);
70 |
71 | Return:
72 |
73 | // no need to unlock or free the resource
74 | return ipStream;
75 | }
76 |
77 | // Now that we have an IStream pointer to the data of the image,
78 | // we can use WIC to load that image. An important step in this
79 | // process is to use WICConvertBitmapSource to ensure that the image
80 | // is in a 32bpp format suitable for direct conversion into a DIB.
81 | // This method assumes that the input image is in the PNG format;
82 | // for a splash screen, this is an excellent choice because it allows
83 | // an alpha channel as well as lossless compression of the source image.
84 | // (To make the splash screen image as small as possible,
85 | // I highly recommend the PNGOUT compression utility.)
86 |
87 | // Loads a PNG image from the specified stream (using Windows Imaging Component).
88 | IWICBitmapSource * LoadBitmapFromStream(IStream * ipImageStream)
89 | {
90 | // initialize return value
91 | IWICBitmapSource * ipBitmap = NULL;
92 |
93 | // load WIC's PNG decoder
94 | IWICBitmapDecoder * ipDecoder = NULL;
95 | IID i = IID_IWICBitmapDecoder;
96 |
97 | if (FAILED(CoCreateInstance(CLSID_WICPngDecoder1, NULL, CLSCTX_INPROC_SERVER,
98 | i,//__uuidof(ipDecoder)
99 |
100 | (void **)&ipDecoder)))
101 | goto Return;
102 |
103 |
104 | // load the PNG
105 | if (FAILED(ipDecoder->Initialize(ipImageStream, WICDecodeMetadataCacheOnLoad)))
106 | goto ReleaseDecoder;
107 |
108 | // check for the presence of the first frame in the bitmap
109 | UINT nFrameCount = 0;
110 |
111 | if (FAILED(ipDecoder->GetFrameCount(&nFrameCount)) || nFrameCount != 1)
112 | goto ReleaseDecoder;
113 |
114 | // load the first frame (i.e., the image)
115 | IWICBitmapFrameDecode * ipFrame = NULL;
116 |
117 | if (FAILED(ipDecoder->GetFrame(0, &ipFrame)))
118 | goto ReleaseDecoder;
119 |
120 |
121 | // convert the image to 32bpp BGRA format with pre-multiplied alpha
122 | // (it may not be stored in that format natively in the PNG resource,
123 | // but we need this format to create the DIB to use on-screen)
124 | WICConvertBitmapSource(GUID_WICPixelFormat32bppPBGRA, ipFrame, &ipBitmap);
125 | ipFrame->Release();
126 |
127 | ReleaseDecoder:
128 | ipDecoder->Release();
129 |
130 | Return:
131 | return ipBitmap;
132 |
133 | }
134 |
135 |
136 | // Creates a 32-bit DIB from the specified WIC bitmap.
137 | HBITMAP CreateHBITMAP(IWICBitmapSource * ipBitmap, PVOID *bits)
138 | {
139 | // initialize return value
140 | HBITMAP hbmp = NULL;
141 |
142 | // get image attributes and check for valid image
143 | UINT width = 0;
144 | UINT height = 0;
145 |
146 | if (FAILED(ipBitmap->GetSize(&width, &height)) || width == 0 || height == 0)
147 | goto Return;
148 |
149 | // prepare structure giving bitmap information (negative height indicates a top-down DIB)
150 | BITMAPINFO bminfo;
151 |
152 | ZeroMemory(&bminfo, sizeof(bminfo));
153 |
154 | bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
155 | bminfo.bmiHeader.biWidth = width;
156 | bminfo.bmiHeader.biHeight = -((LONG)height);
157 | bminfo.bmiHeader.biPlanes = 1;
158 | bminfo.bmiHeader.biBitCount = 32;
159 | bminfo.bmiHeader.biCompression = BI_RGB;
160 |
161 | // create a DIB section that can hold the image
162 | void * pvImageBits = NULL;
163 |
164 | HDC hdcScreen = GetDC(NULL);
165 | hbmp = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0);
166 | ReleaseDC(NULL, hdcScreen);
167 |
168 | if (bits)
169 | *bits = pvImageBits;
170 |
171 | if (hbmp == NULL)
172 | goto Return;
173 |
174 |
175 | // extract the image into the HBITMAP
176 | const UINT cbStride = width * 4;
177 | const UINT cbImage = cbStride * height;
178 |
179 | if (FAILED(ipBitmap->CopyPixels(NULL, cbStride, cbImage, static_cast(pvImageBits))))
180 | {
181 | // couldn't extract image; delete HBITMAP
182 | DeleteObject(hbmp);
183 |
184 | hbmp = NULL;
185 | }
186 |
187 | Return:
188 | return hbmp;
189 | }
190 |
191 | // Loads the PNG bitmap into a 32bit HBITMAP.
192 | extern "C"
193 | HBITMAP LoadPNGImage(UINT id, OUT VOID **bits)
194 | {
195 | HBITMAP hbmpSplash = NULL;
196 |
197 | // load the PNG image data into a stream
198 | IStream * ipImageStream = CreateStreamOnResource(MAKEINTRESOURCE(id), L"PNG");
199 |
200 | if (ipImageStream == NULL)
201 | goto Return;
202 |
203 | // load the bitmap with WIC
204 | IWICBitmapSource * ipBitmap = LoadBitmapFromStream(ipImageStream);
205 |
206 | if (ipBitmap == NULL)
207 | goto ReleaseStream;
208 |
209 | // create a HBITMAP containing the image
210 | hbmpSplash = CreateHBITMAP(ipBitmap, bits);
211 |
212 | ipBitmap->Release();
213 |
214 | ReleaseStream:
215 |
216 | ipImageStream->Release();
217 |
218 | Return:
219 | return hbmpSplash;
220 | }
221 |
--------------------------------------------------------------------------------
/src/GetRemoteWindowInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // GetRemoteClassInfoEx.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // In order to retrieve private class information for a
8 | // window in another process, we have to create
9 | // a remote thread in that process and call GetClassInfoEx from
10 | // there.
11 | //
12 | // GetRemoteClassInfoEx uses the InjectRemoteThread call defined
13 | // in InjectThread.c
14 | //
15 |
16 | #include "WinSpy.h"
17 |
18 | #include
19 | #include "InjectThread.h"
20 |
21 | typedef BOOL(WINAPI *PROCGETCLASSINFOEXW)(HINSTANCE, PCWSTR, WNDCLASSEXW*);
22 | typedef LONG_PTR(WINAPI *PROCGETWINDOWLONGPTR)(HWND, int);
23 | typedef LRESULT(WINAPI *PROCSENDMESSAGETO)(HWND, UINT, WPARAM, LPARAM, UINT, UINT, PDWORD_PTR);
24 |
25 | //
26 | // Define a structure for the remote thread to use
27 | //
28 | typedef struct
29 | {
30 | // Input starts
31 | PROCGETCLASSINFOEXW fnGetClassInfoEx;
32 | PROCGETWINDOWLONGPTR fnGetWindowLongPtr;
33 | PROCSENDMESSAGETO fnSendMessageTimeout;
34 |
35 | HWND hwnd; //window we want to get class info for
36 | ATOM atom; //class atom of window
37 | HINSTANCE hInst;
38 | int nTextSize;
39 |
40 | // Output starts
41 | WNDCLASSEXW wcOutput;
42 | WNDPROC wndproc;
43 |
44 | WCHAR szText[200]; // Window text to retrieve
45 | } INJDATA;
46 |
47 | #pragma runtime_checks("", off)
48 | // calls to the stack checking routine must be disabled
49 | #pragma check_stack(off)
50 |
51 | // From https://msdn.microsoft.com/en-us/library/7977wcck.aspx:
52 | // The order here is important.
53 | // Section names must be 8 characters or less.
54 | // The sections with the same name before the $
55 | // are merged into one section. The order that
56 | // they are merged is determined by sorting
57 | // the characters after the $.
58 | // InitSegStart and InitSegEnd are used to set
59 | // boundaries so we can find the real functions
60 | // that we need to call for initialization.
61 |
62 | //
63 | // Thread to inject to remote process. Must not
64 | // make ANY calls to code in THIS process.
65 | //
66 | __declspec(code_seg(".inject$a"))
67 | // GetDataProc will selectively collect 3 pieces of information about the target window:
68 | // - window procedure
69 | // - window class
70 | // - text
71 | static DWORD WINAPI GetDataProc(PVOID pParam)
72 | {
73 | INJDATA *pInjData = (INJDATA *)pParam;
74 | BOOL fRet = TRUE;
75 | DWORD_PTR dwpResult;
76 |
77 | if (pInjData->fnGetWindowLongPtr)
78 | pInjData->wndproc = (WNDPROC)pInjData->fnGetWindowLongPtr(pInjData->hwnd, GWLP_WNDPROC);
79 |
80 | if (pInjData->fnGetClassInfoEx)
81 | {
82 | static_assert(sizeof(pInjData->wcOutput) == sizeof(WNDCLASSEXA), "Unicode and ANSI structures expected to be the same size");
83 | pInjData->wcOutput.cbSize = sizeof(pInjData->wcOutput);
84 | fRet = fRet && pInjData->fnGetClassInfoEx(pInjData->hInst, (PCWSTR)(intptr_t)pInjData->atom, &pInjData->wcOutput);
85 | }
86 |
87 | if (pInjData->fnSendMessageTimeout)
88 | {
89 | // Null-terminate in case the gettext fails
90 | pInjData->szText[0] = L'\0';
91 |
92 | pInjData->fnSendMessageTimeout(pInjData->hwnd, WM_GETTEXT,
93 | pInjData->nTextSize, (LPARAM)pInjData->szText,
94 | SMTO_ABORTIFHUNG, 100, &dwpResult);
95 | }
96 |
97 | return fRet;
98 |
99 | }
100 |
101 | __declspec(code_seg(".inject$z"))
102 | static void AfterGetDataProc(void) { }
103 |
104 | #pragma check_stack
105 | #pragma runtime_checks("", restore)
106 |
107 | BOOL IsInsideModule(MODULEINFO *pModuleInfo, PVOID fn)
108 | {
109 | return !fn || (pModuleInfo->lpBaseOfDll <= fn && (PVOID)((BYTE*)pModuleInfo->lpBaseOfDll + pModuleInfo->SizeOfImage) > fn);
110 | }
111 |
112 | BOOL IsInjectionDataValid(INJDATA *pInjData)
113 | {
114 | // It is only safe to inject this code if we are passing the addresses of functions in user32.dll (which is shared across all processes).
115 | // If an appcompat shim is applied to our process that replaces any of these functions' pointers with ones outside of user32.dll,
116 | // we cannot really do anything about this (as it will also override the GetProcAddress behavior), so the best we can do is fail gracefully
117 | HMODULE hModUser32 = GetModuleHandle(L"user32.dll");
118 | if (!hModUser32)
119 | return FALSE;
120 |
121 | MODULEINFO moduleInfo;
122 | if (!GetModuleInformation(GetCurrentProcess(), hModUser32, &moduleInfo, sizeof(moduleInfo)))
123 | return FALSE;
124 |
125 | return (IsInsideModule(&moduleInfo, (PVOID)(intptr_t)pInjData->fnSendMessageTimeout) &&
126 | IsInsideModule(&moduleInfo, (PVOID)(intptr_t)pInjData->fnGetWindowLongPtr) &&
127 | IsInsideModule(&moduleInfo, (PVOID)(intptr_t)pInjData->fnGetClassInfoEx));
128 | }
129 |
130 | BOOL GetRemoteWindowInfo(HWND hwnd, WNDCLASSEX *pClass, WNDPROC *pProc, WCHAR *pszText, int nTextLen)
131 | {
132 | INJDATA InjData;
133 | BOOL fReturn;
134 |
135 | // Calculate how many bytes the injected code takes
136 | DWORD_PTR cbCodeSize = ((BYTE *)(intptr_t)AfterGetDataProc - (BYTE *)(intptr_t)GetDataProc);
137 |
138 | //
139 | // Setup the injection structure:
140 | //
141 | ZeroMemory(&InjData, sizeof(InjData));
142 |
143 | // Get pointers to the API calls we will be using in the remote thread
144 | if (pszText)
145 | InjData.fnSendMessageTimeout = SendMessageTimeout;
146 | if (pProc)
147 | InjData.fnGetWindowLongPtr = IsWindowUnicode(hwnd) ? GetWindowLongPtrW : GetWindowLongPtrA;
148 | if (pClass)
149 | InjData.fnGetClassInfoEx = IsWindowUnicode(hwnd) ? GetClassInfoExW : (PROCGETCLASSINFOEXW)GetClassInfoExA;
150 |
151 | // Setup the data the API calls will need
152 | InjData.hwnd = (HWND)hwnd;
153 | InjData.atom = (ATOM)GetClassLong(hwnd, GCW_ATOM);
154 | InjData.hInst = (HINSTANCE)GetClassLongPtr(hwnd, GCLP_HMODULE);
155 | InjData.nTextSize = ARRAYSIZE(InjData.szText);
156 | InjData.wndproc = 0;
157 |
158 | //
159 | // Inject the GetClassInfoExProc function, and our InjData structure!
160 | //
161 | #define offsetof(s,m) ((size_t)&(((s*)0)->m))
162 | fReturn = IsInjectionDataValid(&InjData) && InjectRemoteThread(hwnd, GetDataProc, cbCodeSize, &InjData, sizeof(InjData), offsetof(INJDATA, wcOutput));
163 |
164 | if (fReturn == FALSE)
165 | {
166 | // Failed to retrieve class information!
167 | if (pProc)
168 | *pProc = NULL;
169 | if (pClass)
170 | ZeroMemory(pClass, sizeof(WNDCLASSEX));
171 | if (pszText)
172 | pszText[0] = 0;
173 | return FALSE;
174 | }
175 | else
176 | {
177 | if (pClass)
178 | {
179 | *pClass = InjData.wcOutput;
180 | // As these pointers come from another process, zero them out to avoid accidental misuse
181 | pClass->lpszClassName = pClass->lpszMenuName = NULL;
182 | }
183 |
184 | if (pProc)
185 | *pProc = InjData.wndproc;
186 |
187 | if (pszText)
188 | StringCchCopy(pszText, nTextLen, InjData.szText);
189 | return TRUE;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/EditSize.c:
--------------------------------------------------------------------------------
1 | //
2 | // Edit Size Dialog.
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Just a simple dialog which allows you to edit a
8 | // window's size / position
9 | //
10 |
11 | #include "WinSpy.h"
12 |
13 | #include "resource.h"
14 |
15 | //
16 | // rect - window coords
17 | //
18 | void SetupEdits(HWND hwndDlg, HWND hwndTarget, RECT *prect)
19 | {
20 | DWORD dwStyle;
21 | RECT rect;
22 |
23 | CopyRect(&rect, prect);
24 |
25 | // Is this window a child control or not??
26 | dwStyle = GetWindowLong(hwndTarget, GWL_STYLE);
27 |
28 | // If this is a child window, then make its coords
29 | // relative to its parent.
30 | if (dwStyle & WS_CHILD)
31 | MapWindowPoints(NULL, GetParent(hwndTarget), (POINT *)&rect, 2);
32 |
33 | // Set the edit control's contents
34 | SetDlgItemInt(hwndDlg, IDC_EDITX, rect.left, TRUE);
35 | SetDlgItemInt(hwndDlg, IDC_EDITY, rect.top, TRUE);
36 | SetDlgItemInt(hwndDlg, IDC_EDITW, GetRectWidth(&rect), TRUE);
37 | SetDlgItemInt(hwndDlg, IDC_EDITH, GetRectHeight(&rect), TRUE);
38 | }
39 |
40 | //
41 | // Align the target window's size/pos and update the
42 | // contents of the edit boxes
43 | //
44 | void AlignTargetPos(HWND hwndDlg, HWND hwndTarget, UINT alignCommand)
45 | {
46 | int x = GetDlgItemInt(hwndDlg, IDC_EDITX, 0, TRUE);
47 | int y = GetDlgItemInt(hwndDlg, IDC_EDITY, 0, TRUE);
48 | int w = GetDlgItemInt(hwndDlg, IDC_EDITW, 0, TRUE);
49 | int h = GetDlgItemInt(hwndDlg, IDC_EDITH, 0, TRUE);
50 |
51 | // Is this window a child control or not??
52 | DWORD dwStyle = GetWindowLong(hwndTarget, GWL_STYLE);
53 |
54 | // If this is a child window, then make its coords
55 | // relative to its parent.
56 | RECT containerRect;
57 | if (dwStyle & WS_CHILD)
58 | {
59 | HWND hwndParent = GetParent(hwndTarget);
60 | GetClientRect(hwndParent, &containerRect);
61 | }
62 | else
63 | {
64 | RECT rect = {x, y, x + w, y + h};
65 | HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
66 |
67 | MONITORINFO monitorInfo = { sizeof(monitorInfo) };
68 | GetMonitorInfo(monitor, &monitorInfo);
69 |
70 | CopyRect(&containerRect, &monitorInfo.rcWork);
71 | }
72 |
73 | switch (alignCommand) {
74 | case IDC_ADJUSTWINPOS_ALIGN_TOP:
75 | y = containerRect.top;
76 | break;
77 |
78 | case IDC_ADJUSTWINPOS_ALIGN_LEFT:
79 | x = containerRect.left;
80 | break;
81 |
82 | case IDC_ADJUSTWINPOS_ALIGN_CENTER:
83 | x = containerRect.left + (containerRect.right - containerRect.left - w) / 2;
84 | y = containerRect.top + (containerRect.bottom - containerRect.top - h) / 2;
85 | break;
86 |
87 | case IDC_ADJUSTWINPOS_ALIGN_RIGHT:
88 | x = containerRect.right - w;
89 | break;
90 |
91 | case IDC_ADJUSTWINPOS_ALIGN_BOTTOM:
92 | y = containerRect.bottom - h;
93 | break;
94 | }
95 |
96 | // Set the edit control's contents
97 | SetDlgItemInt(hwndDlg, IDC_EDITX, x, TRUE);
98 | SetDlgItemInt(hwndDlg, IDC_EDITY, y, TRUE);
99 | SetDlgItemInt(hwndDlg, IDC_EDITW, w, TRUE);
100 | SetDlgItemInt(hwndDlg, IDC_EDITH, h, TRUE);
101 | }
102 |
103 | //
104 | // Set the target window's size/pos, based on the
105 | // contents of the edit boxes
106 | //
107 | void SetTargetPos(HWND hwndDlg, HWND hwndTarget)
108 | {
109 | int x = GetDlgItemInt(hwndDlg, IDC_EDITX, 0, TRUE);
110 | int y = GetDlgItemInt(hwndDlg, IDC_EDITY, 0, TRUE);
111 | int w = GetDlgItemInt(hwndDlg, IDC_EDITW, 0, TRUE);
112 | int h = GetDlgItemInt(hwndDlg, IDC_EDITH, 0, TRUE);
113 |
114 | SetWindowPos(hwndTarget, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER);
115 | }
116 |
117 | //
118 | // Dialog procedure for the edit size window
119 | //
120 | INT_PTR CALLBACK EditSizeDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
121 | {
122 | RECT rect;
123 |
124 | static HWND hwndTarget; // target window!
125 | static RECT rect0; // original coords
126 |
127 | switch (iMsg)
128 | {
129 | case WM_INITDIALOG:
130 |
131 | hwndTarget = (HWND)lParam;
132 |
133 | GetWindowRect(hwndTarget, &rect0);
134 | CopyRect(&rect, &rect0);
135 |
136 | SetupEdits(hwnd, hwndTarget, &rect);
137 |
138 | // Set up the spin controls
139 | SendDlgItemMessage(hwnd, IDC_SPINX, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, UD_MINVAL));
140 | SendDlgItemMessage(hwnd, IDC_SPINY, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, UD_MINVAL));
141 | SendDlgItemMessage(hwnd, IDC_SPINW, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, UD_MINVAL));
142 | SendDlgItemMessage(hwnd, IDC_SPINH, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, UD_MINVAL));
143 |
144 | return TRUE;
145 |
146 | case WM_CLOSE:
147 | EndDialog(hwnd, 0);
148 | return TRUE;
149 |
150 | case WM_VSCROLL:
151 |
152 | SetTargetPos(hwnd, hwndTarget);
153 |
154 | // Get the window's coords again to see what happened
155 | GetWindowRect(hwndTarget, &rect);
156 | SetupEdits(hwnd, hwndTarget, &rect);
157 |
158 | return TRUE;
159 |
160 | case WM_COMMAND:
161 | switch (LOWORD(wParam))
162 | {
163 | case IDC_RESET:
164 |
165 | // go back to the original coords
166 | SetupEdits(hwnd, hwndTarget, &rect0);
167 | SetTargetPos(hwnd, hwndTarget);
168 |
169 | // Get the window's coords again to see what happened
170 | GetWindowRect(hwndTarget, &rect);
171 | SetupEdits(hwnd, hwndTarget, &rect);
172 |
173 | return TRUE;
174 |
175 | case IDC_ADJUST:
176 |
177 | SetTargetPos(hwnd, hwndTarget);
178 |
179 | // Get the window's coords again to see what happened
180 | GetWindowRect(hwndTarget, &rect);
181 | SetupEdits(hwnd, hwndTarget, &rect);
182 |
183 | return TRUE;
184 |
185 | case IDC_ADJUSTWINPOS_ALIGN_TOP:
186 | case IDC_ADJUSTWINPOS_ALIGN_LEFT:
187 | case IDC_ADJUSTWINPOS_ALIGN_CENTER:
188 | case IDC_ADJUSTWINPOS_ALIGN_RIGHT:
189 | case IDC_ADJUSTWINPOS_ALIGN_BOTTOM:
190 |
191 | AlignTargetPos(hwnd, hwndTarget, LOWORD(wParam));
192 | SetTargetPos(hwnd, hwndTarget);
193 |
194 | // Get the window's coords again to see what happened
195 | GetWindowRect(hwndTarget, &rect);
196 | SetupEdits(hwnd, hwndTarget, &rect);
197 |
198 | return TRUE;
199 |
200 | case IDCANCEL:
201 | EndDialog(hwnd, 0);
202 | return TRUE;
203 |
204 | }
205 |
206 | return FALSE;
207 | }
208 |
209 | return FALSE;
210 | }
211 |
212 |
213 | void ShowEditSizeDlg(HWND hwndParent, HWND hwndTarget)
214 | {
215 | if (IsWindow(g_hCurWnd))
216 | {
217 | DialogBoxParam(
218 | g_hInst,
219 | MAKEINTRESOURCE(IDD_ADJUSTWINPOS),
220 | hwndParent,
221 | EditSizeDlgProc,
222 | (LPARAM)hwndTarget);
223 |
224 | UpdateGeneralTab(hwndTarget);
225 | }
226 | else
227 | {
228 | MessageBox(hwndParent,
229 | L"Not a valid window",
230 | szAppName,
231 | MB_OK | MB_ICONEXCLAMATION);
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/src/winspy.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 | Source Files
32 |
33 |
34 | Source Files
35 |
36 |
37 | Source Files
38 |
39 |
40 | Source Files
41 |
42 |
43 | Source Files
44 |
45 |
46 | Source Files
47 |
48 |
49 | Source Files
50 |
51 |
52 | Source Files
53 |
54 |
55 | Source Files
56 |
57 |
58 | Source Files
59 |
60 |
61 | Source Files
62 |
63 |
64 | Source Files
65 |
66 |
67 | Source Files
68 |
69 |
70 | Source Files
71 |
72 |
73 | Source Files
74 |
75 |
76 | Source Files
77 |
78 |
79 | Source Files
80 |
81 |
82 | Source Files
83 |
84 |
85 | Source Files
86 |
87 |
88 | Source Files
89 |
90 |
91 | Source Files
92 |
93 |
94 | Source Files
95 |
96 |
97 | Source Files
98 |
99 |
100 | Source Files
101 |
102 |
103 | Source Files
104 |
105 |
106 | Source Files
107 |
108 |
109 | Source Files
110 |
111 |
112 | Source Files
113 |
114 |
115 |
116 |
117 | Header Files
118 |
119 |
120 | Header Files
121 |
122 |
123 | Header Files
124 |
125 |
126 | Header Files
127 |
128 |
129 | Header Files
130 |
131 |
132 | Header Files
133 |
134 |
135 | Header Files
136 |
137 |
138 | Header Files
139 |
140 |
141 | Header Files
142 |
143 |
144 |
145 |
146 | Source Files
147 |
148 |
149 |
150 |
151 | Resource Files
152 |
153 |
154 | Resource Files
155 |
156 |
157 | Resource Files
158 |
159 |
160 |
161 |
162 | Resource Files
163 |
164 |
165 |
166 |
167 | Resource Files
168 |
169 |
170 | Resource Files
171 |
172 |
173 | Resource Files
174 |
175 |
176 | Resource Files
177 |
178 |
179 | Resource Files
180 |
181 |
182 | Resource Files
183 |
184 |
185 | Resource Files
186 |
187 |
188 | Resource Files
189 |
190 |
191 | Resource Files
192 |
193 |
194 | Resource Files
195 |
196 |
197 | Resource Files
198 |
199 |
200 | Resource Files
201 |
202 |
203 | Resource Files
204 |
205 |
206 | Resource Files
207 |
208 |
209 |
--------------------------------------------------------------------------------
/src/Options.c:
--------------------------------------------------------------------------------
1 | //
2 | // Options.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Implements the Options dialog for WinSpy
8 | //
9 |
10 | #include "WinSpy.h"
11 | #include "RegHelper.h"
12 | #include "resource.h"
13 | #include "utils.h"
14 |
15 | Options g_opts;
16 |
17 | #define REG_BASESTR L"Software\\Catch22\\WinSpy++ 1.5"
18 |
19 | static WCHAR szRegLoc[] = REG_BASESTR;
20 |
21 | void LoadSettings(void)
22 | {
23 | HKEY hkey;
24 |
25 | RegCreateKeyEx(HKEY_CURRENT_USER, szRegLoc, 0, L"", 0, KEY_READ, NULL, &hkey, NULL);
26 |
27 | g_opts.fSaveWinPos = GetSettingBool(hkey, L"SavePosition", TRUE);
28 | g_opts.fAlwaysOnTop = GetSettingBool(hkey, L"AlwaysOnTop", FALSE);
29 | g_opts.fMinimizeWinSpy = GetSettingBool(hkey, L"MinimizeWinSpy", TRUE);
30 | g_opts.fFullDragging = GetSettingBool(hkey, L"FullDragging", TRUE);
31 | g_opts.fShowHidden = GetSettingBool(hkey, L"ShowHidden", FALSE);
32 | g_opts.fShowDimmed = GetSettingBool(hkey, L"ShowDimmed", TRUE);
33 | g_opts.fClassThenText = GetSettingBool(hkey, L"ClassThenText", TRUE);
34 | g_opts.fPinWindow = GetSettingBool(hkey, L"PinWindow", FALSE);
35 | g_opts.fShowInCaption = GetSettingBool(hkey, L"ShowInCaption", TRUE);
36 | g_opts.fEnableToolTips = GetSettingBool(hkey, L"EnableToolTips", FALSE);
37 | g_opts.fShowDesktopRoot = GetSettingBool(hkey, L"ShowDesktopRoot", FALSE);
38 | g_opts.uTreeInclude = GetSettingInt(hkey, L"TreeItems", WINLIST_INCLUDE_ALL);
39 | g_opts.fShowHiddenInList = GetSettingBool(hkey, L"List_ShowHidden", TRUE);
40 | g_opts.fEnableHotkey = GetSettingBool(hkey, L"EnableHotkey", FALSE);
41 |
42 | g_opts.uPinnedCorner = GetSettingInt(hkey, L"PinCorner", 0);
43 |
44 | g_opts.ptPinPos.x = GetSettingInt(hkey, L"xpos", CW_USEDEFAULT);
45 | g_opts.ptPinPos.y = GetSettingInt(hkey, L"ypos", CW_USEDEFAULT);
46 |
47 | // If the hotkey value isn't there, or is invalid, default to tilde '~'
48 |
49 | int value = GetSettingInt(hkey, L"Hotkey", 0);
50 |
51 | if ((value > 0xFFFF) || (value <= 0))
52 | {
53 | g_opts.fEnableHotkey = FALSE;
54 | value = VK_OEM_3;
55 | }
56 |
57 | g_opts.wHotkey = (WORD)value;
58 |
59 | // Ignore the saved window position if it no longer lies within the
60 | // bounds of a monitor.
61 |
62 | if (g_opts.fSaveWinPos && (g_opts.ptPinPos.x != CW_USEDEFAULT) && (g_opts.ptPinPos.y != CW_USEDEFAULT))
63 | {
64 | if (!MonitorFromPoint(g_opts.ptPinPos, MONITOR_DEFAULTTONULL))
65 | {
66 | g_opts.ptPinPos.x = CW_USEDEFAULT;
67 | g_opts.ptPinPos.y = CW_USEDEFAULT;
68 | }
69 | }
70 |
71 | RegCloseKey(hkey);
72 | }
73 |
74 | void SaveSettings(void)
75 | {
76 | HKEY hkey;
77 |
78 | RegCreateKeyEx(HKEY_CURRENT_USER, szRegLoc, 0, L"", 0, KEY_WRITE, NULL, &hkey, NULL);
79 |
80 | WriteSettingBool(hkey, L"SavePosition", g_opts.fSaveWinPos);
81 | WriteSettingBool(hkey, L"AlwaysOnTop", g_opts.fAlwaysOnTop);
82 | WriteSettingBool(hkey, L"MinimizeWinSpy", g_opts.fMinimizeWinSpy);
83 | WriteSettingBool(hkey, L"FullDragging", g_opts.fFullDragging);
84 | WriteSettingBool(hkey, L"ShowHidden", g_opts.fShowHidden);
85 | WriteSettingBool(hkey, L"ShowDimmed", g_opts.fShowDimmed);
86 | WriteSettingBool(hkey, L"ClassThenText", g_opts.fClassThenText);
87 | WriteSettingBool(hkey, L"PinWindow", g_opts.fPinWindow);
88 | WriteSettingBool(hkey, L"ShowInCaption", g_opts.fShowInCaption);
89 | WriteSettingBool(hkey, L"EnableToolTips", g_opts.fEnableToolTips);
90 | WriteSettingBool(hkey, L"ShowDesktopRoot", g_opts.fShowDesktopRoot);
91 | WriteSettingBool(hkey, L"List_ShowHidden", g_opts.fShowHiddenInList);
92 | WriteSettingInt(hkey, L"TreeItems", g_opts.uTreeInclude);
93 | WriteSettingInt(hkey, L"PinCorner", g_opts.uPinnedCorner);
94 |
95 | WriteSettingInt(hkey, L"xpos", g_opts.ptPinPos.x);
96 | WriteSettingInt(hkey, L"ypos", g_opts.ptPinPos.y);
97 |
98 | if (g_fFirstInstance)
99 | {
100 | WriteSettingBool(hkey, L"EnableHotkey", g_opts.fEnableHotkey);
101 | WriteSettingInt(hkey, L"Hotkey", g_opts.wHotkey);
102 | }
103 |
104 | RegCloseKey(hkey);
105 | }
106 |
107 | void OptionsDlg_UpdateHotkeyControls(HWND hwndDlg)
108 | {
109 | if (!g_fFirstInstance)
110 | {
111 | EnableDlgItem(hwndDlg, IDC_OPTIONS_WIN_LABEL, FALSE);
112 | EnableDlgItem(hwndDlg, IDC_OPTIONS_ENABLE_HOTKEY, FALSE);
113 | EnableDlgItem(hwndDlg, IDC_HOTKEY, FALSE);
114 | }
115 | else
116 | {
117 | BOOL fEnabled = IsDlgButtonChecked(hwndDlg, IDC_OPTIONS_ENABLE_HOTKEY);
118 |
119 | EnableDlgItem(hwndDlg, IDC_OPTIONS_WIN_LABEL, fEnabled);
120 | EnableDlgItem(hwndDlg, IDC_HOTKEY, fEnabled);
121 | }
122 | }
123 |
124 |
125 | INT_PTR CALLBACK OptionsDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
126 | {
127 | UNREFERENCED_PARAMETER(lParam);
128 | static HWND hwndTarget;
129 |
130 | switch (iMsg)
131 | {
132 | case WM_INITDIALOG:
133 | CheckDlgButton(hwnd, IDC_OPTIONS_SAVEPOS, g_opts.fSaveWinPos);
134 | CheckDlgButton(hwnd, IDC_OPTIONS_FULLDRAG, g_opts.fFullDragging);
135 | CheckDlgButton(hwnd, IDC_OPTIONS_DIR, g_opts.fClassThenText);
136 | CheckDlgButton(hwnd, IDC_OPTIONS_SHOWHIDDEN, g_opts.fShowDimmed);
137 | CheckDlgButton(hwnd, IDC_OPTIONS_SHOWINCAPTION, g_opts.fShowInCaption);
138 | CheckDlgButton(hwnd, IDC_OPTIONS_TOOLTIPS, g_opts.fEnableToolTips);
139 | CheckDlgButton(hwnd, IDC_OPTIONS_DESKTOPROOT, g_opts.fShowDesktopRoot);
140 | CheckDlgButton(hwnd, IDC_OPTIONS_LIST_SHOWHIDDEN, g_opts.fShowHiddenInList);
141 | CheckDlgButton(hwnd, IDC_OPTIONS_ENABLE_HOTKEY, g_opts.fEnableHotkey);
142 |
143 | CheckDlgButton(hwnd, IDC_OPTIONS_INCHANDLE,
144 | (g_opts.uTreeInclude & WINLIST_INCLUDE_HANDLE) ? TRUE : FALSE);
145 |
146 | CheckDlgButton(hwnd, IDC_OPTIONS_INCCLASS,
147 | (g_opts.uTreeInclude & WINLIST_INCLUDE_CLASS) ? TRUE : FALSE);
148 |
149 | OptionsDlg_UpdateHotkeyControls(hwnd);
150 |
151 | SendDlgItemMessage(hwnd, IDC_HOTKEY, HKM_SETHOTKEY, g_opts.wHotkey, 0);
152 |
153 | return TRUE;
154 |
155 | case WM_CLOSE:
156 | EndDialog(hwnd, 0);
157 | return TRUE;
158 |
159 | case WM_COMMAND:
160 | switch (LOWORD(wParam))
161 | {
162 | case IDC_OPTIONS_ENABLE_HOTKEY:
163 | OptionsDlg_UpdateHotkeyControls(hwnd);
164 | return TRUE;
165 |
166 | case IDOK:
167 |
168 | g_opts.fSaveWinPos = IsDlgButtonChecked(hwnd, IDC_OPTIONS_SAVEPOS);
169 | g_opts.fFullDragging = IsDlgButtonChecked(hwnd, IDC_OPTIONS_FULLDRAG);
170 | g_opts.fClassThenText = IsDlgButtonChecked(hwnd, IDC_OPTIONS_DIR);
171 | g_opts.fShowDimmed = IsDlgButtonChecked(hwnd, IDC_OPTIONS_SHOWHIDDEN);
172 | g_opts.fShowInCaption = IsDlgButtonChecked(hwnd, IDC_OPTIONS_SHOWINCAPTION);
173 | g_opts.fEnableToolTips = IsDlgButtonChecked(hwnd, IDC_OPTIONS_TOOLTIPS);
174 | g_opts.fShowDesktopRoot = IsDlgButtonChecked(hwnd, IDC_OPTIONS_DESKTOPROOT);
175 | g_opts.fShowHiddenInList = IsDlgButtonChecked(hwnd, IDC_OPTIONS_LIST_SHOWHIDDEN);
176 | g_opts.fEnableHotkey = IsDlgButtonChecked(hwnd, IDC_OPTIONS_ENABLE_HOTKEY);
177 | g_opts.wHotkey = (WORD)SendDlgItemMessage(hwnd, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0);
178 |
179 | g_opts.uTreeInclude = 0;
180 |
181 | if (IsDlgButtonChecked(hwnd, IDC_OPTIONS_INCHANDLE))
182 | g_opts.uTreeInclude |= WINLIST_INCLUDE_HANDLE;
183 |
184 | if (IsDlgButtonChecked(hwnd, IDC_OPTIONS_INCCLASS))
185 | g_opts.uTreeInclude |= WINLIST_INCLUDE_CLASS;
186 |
187 | EndDialog(hwnd, 0);
188 | return TRUE;
189 |
190 | case IDCANCEL:
191 | EndDialog(hwnd, 0);
192 | return TRUE;
193 | }
194 |
195 | return FALSE;
196 | }
197 |
198 | return FALSE;
199 | }
200 |
201 |
202 | void ShowOptionsDlg(HWND hwndParent)
203 | {
204 | DialogBox(g_hInst, MAKEINTRESOURCE(IDD_OPTIONS), hwndParent, OptionsDlgProc);
205 |
206 | UpdateMainWindowText();
207 |
208 | UpdateGlobalHotkey();
209 |
210 | SendMessage(g_hwndToolTip, TTM_ACTIVATE, g_opts.fEnableToolTips, 0);
211 | }
212 |
--------------------------------------------------------------------------------
/src/WinSpyCommand.c:
--------------------------------------------------------------------------------
1 | //
2 | // WinSpyCommand.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Menu / Control Command handler
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | #include "resource.h"
13 | #include "Utils.h"
14 | #include "FindTool.h"
15 | #include "CaptureWindow.h"
16 |
17 | void SetPinState(BOOL fPinned)
18 | {
19 | g_opts.fPinWindow = fPinned;
20 |
21 | SendMessage(g_hwndPin, TB_CHANGEBITMAP, IDM_WINSPY_PIN,
22 | MAKELPARAM(fPinned, 0));
23 |
24 | SendMessage(g_hwndPin, TB_CHECKBUTTON, IDM_WINSPY_PIN,
25 | MAKELPARAM(fPinned, 0));
26 |
27 | }
28 |
29 | UINT WinSpyDlg_CommandHandler(HWND hwnd, WPARAM wParam, LPARAM lParam)
30 | {
31 | NMHDR hdr;
32 | UINT uLayout;
33 |
34 | HWND hwndGeneral;
35 | HWND hwndFocus;
36 | HWND hwndCtrl;
37 |
38 | switch (LOWORD(wParam))
39 | {
40 | case IDM_WINSPY_ONTOP:
41 |
42 | if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
43 | {
44 | // Not top-most any more
45 | SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_ZONLY);
46 | }
47 | else
48 | {
49 | // Make top-most (float above all other windows)
50 | SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_ZONLY);
51 | }
52 |
53 | return TRUE;
54 |
55 | case IDM_WINSPY_TOGGLE:
56 | ToggleWindowLayout(hwnd);
57 | return TRUE;
58 |
59 | case IDM_WINSPY_TOGGLEEXP:
60 |
61 | uLayout = GetWindowLayout(hwnd);
62 |
63 | if (uLayout == WINSPY_EXPANDED)
64 | SetWindowLayout(hwnd, WINSPY_MINIMIZED);
65 |
66 | else if (uLayout == WINSPY_NORMAL)
67 | SetWindowLayout(hwnd, WINSPY_EXPANDED);
68 |
69 | else if (uLayout == WINSPY_MINIMIZED)
70 | SetWindowLayout(hwnd, WINSPY_NORMAL);
71 |
72 | return TRUE;
73 |
74 | case IDM_WINSPY_ZOOMTL:
75 | WinSpy_ZoomTo(hwnd, PINNED_TOPLEFT);
76 | return TRUE;
77 |
78 | case IDM_WINSPY_ZOOMTR:
79 | WinSpy_ZoomTo(hwnd, PINNED_TOPRIGHT);
80 | return TRUE;
81 |
82 | case IDM_WINSPY_ZOOMBR:
83 | WinSpy_ZoomTo(hwnd, PINNED_BOTTOMRIGHT);
84 | return TRUE;
85 |
86 | case IDM_WINSPY_ZOOMBL:
87 | WinSpy_ZoomTo(hwnd, PINNED_BOTTOMLEFT);
88 | return TRUE;
89 |
90 | case IDM_WINSPY_REFRESH:
91 | DisplayWindowInfo(g_hCurWnd);
92 | return TRUE;
93 |
94 | case IDM_WINSPY_OPTIONS:
95 | ShowOptionsDlg(hwnd);
96 | return TRUE;
97 |
98 | case IDM_WINSPY_PIN:
99 |
100 | g_opts.fPinWindow = !g_opts.fPinWindow;
101 |
102 | SendMessage(g_hwndPin, TB_CHANGEBITMAP, IDM_WINSPY_PIN,
103 | MAKELPARAM(g_opts.fPinWindow, 0));
104 |
105 | // if from an accelerator, then we have to manually check the
106 | if (HIWORD(wParam) == 1)
107 | {
108 | SendMessage(g_hwndPin, TB_CHECKBUTTON, IDM_WINSPY_PIN,
109 | MAKELPARAM(g_opts.fPinWindow, 0));
110 | }
111 |
112 | GetPinnedPosition(hwnd, &g_opts.ptPinPos);
113 | return TRUE;
114 |
115 | case IDC_HIDDEN:
116 | g_opts.fShowHidden = IsDlgButtonChecked(hwnd, IDC_HIDDEN);
117 | return TRUE;
118 |
119 | case IDC_MINIMIZE:
120 | g_opts.fMinimizeWinSpy = IsDlgButtonChecked(hwnd, IDC_MINIMIZE);
121 | return TRUE;
122 |
123 | case IDM_GOTO_TAB_GENERAL:
124 | case IDM_GOTO_TAB_STYLES:
125 | case IDM_GOTO_TAB_PROPERTIES:
126 | case IDM_GOTO_TAB_CLASS:
127 | case IDM_GOTO_TAB_WINDOWS:
128 | case IDM_GOTO_TAB_PROCESS:
129 | case IDM_GOTO_TAB_DPI:
130 |
131 | // Simulate the tab-control being clicked
132 | hdr.hwndFrom = GetDlgItem(hwnd, IDC_TAB1);
133 | hdr.idFrom = IDC_TAB1;
134 | hdr.code = TCN_SELCHANGE;
135 |
136 | TabCtrl_SetCurSel(hdr.hwndFrom, LOWORD(wParam) - IDM_GOTO_TAB_GENERAL);
137 |
138 | SendMessage(hwnd, WM_NOTIFY, 0, (LPARAM)&hdr);
139 |
140 | return TRUE;
141 |
142 | case IDC_FLASH:
143 |
144 | HWND hwndSelected = WindowTree_GetSelectedWindow();
145 |
146 | if (hwndSelected)
147 | {
148 | FlashWindowBorder(hwndSelected);
149 | }
150 |
151 | return TRUE;
152 |
153 | case IDC_EXPAND:
154 |
155 | if (GetWindowLayout(hwnd) == WINSPY_NORMAL)
156 | {
157 | WindowTree_Refresh(g_hCurWnd, FALSE);
158 | SetWindowLayout(hwnd, WINSPY_EXPANDED);
159 | }
160 | else
161 | {
162 | SetWindowLayout(hwnd, WINSPY_NORMAL);
163 | }
164 |
165 | return TRUE;
166 |
167 | case IDC_CAPTURE:
168 | CaptureWindow(hwnd, g_hCurWnd);
169 | MessageBox(hwnd, L"Window contents captured to clipboard", szAppName, MB_ICONINFORMATION);
170 | return TRUE;
171 |
172 | case IDC_AUTOUPDATE:
173 | if (IsDlgButtonChecked(hwnd, IDC_AUTOUPDATE))
174 | SetTimer(hwnd, 0, 1000, NULL);
175 | else
176 | KillTimer(hwnd, 0);
177 | return TRUE;
178 |
179 | case IDOK:
180 |
181 | hwndGeneral = WinSpyTab[GENERAL_TAB].hwnd;
182 | hwndFocus = GetFocus();
183 |
184 | if (hwndFocus == GetDlgItem(hwndGeneral, IDC_HANDLE))
185 | {
186 | hwndCtrl = (HWND)GetDlgItemBaseInt(hwndGeneral, IDC_HANDLE, 16);
187 |
188 | if (IsWindow(hwndCtrl))
189 | {
190 | g_hCurWnd = hwndCtrl;
191 | DisplayWindowInfo(g_hCurWnd);
192 | }
193 |
194 | return 0;
195 | }
196 | else if (hwndFocus == GetDlgItem(hwndGeneral, IDC_CAPTION1) ||
197 | hwndFocus == GetWindow(GetDlgItem(hwndGeneral, IDC_CAPTION2), GW_CHILD))
198 | {
199 | PostMessage(hwndGeneral, WM_COMMAND, MAKEWPARAM(IDC_SETCAPTION, BN_CLICKED), 0);
200 | return FALSE;
201 | }
202 |
203 |
204 | if (GetFocus() != (HWND)lParam)
205 | return FALSE;
206 |
207 | ExitWinSpy(hwnd, 0);
208 |
209 | return TRUE;
210 |
211 | case IDC_LOCATE:
212 |
213 | if (g_hCurWnd)
214 | {
215 | WindowTree_Locate(g_hCurWnd);
216 | }
217 |
218 | return TRUE;
219 |
220 | case IDC_REFRESH:
221 |
222 | WindowTree_Refresh(g_hCurWnd, TRUE);
223 |
224 | return TRUE;
225 | }
226 |
227 | return FALSE;
228 | }
229 |
230 | UINT WinSpyDlg_SysMenuHandler(HWND hwnd, WPARAM wParam, LPARAM lParam)
231 | {
232 | switch (wParam & 0xFFF0)
233 | {
234 | case SC_RESTORE:
235 |
236 | if (IsWindowMinimized(hwnd))
237 | {
238 | break;
239 | }
240 | else
241 | {
242 | ToggleWindowLayout(hwnd);
243 | return 1;
244 | }
245 |
246 | case SC_MAXIMIZE:
247 | ToggleWindowLayout(hwnd);
248 | return 1;
249 | }
250 |
251 | switch (wParam)
252 | {
253 | case IDM_WINSPY_ABOUT:
254 | ShowAboutDlg(hwnd);
255 | return TRUE;
256 |
257 | case IDM_WINSPY_OPTIONS:
258 | ShowOptionsDlg(hwnd);
259 | return TRUE;
260 |
261 | case IDM_WINSPY_BROADCASTER:
262 | ShowBroadcasterDlg(hwnd);
263 | return TRUE;
264 |
265 | case IDM_WINSPY_ONTOP:
266 | PostMessage(hwnd, WM_COMMAND, wParam, lParam);
267 | return TRUE;
268 |
269 | }
270 | return FALSE;
271 |
272 | }
273 |
274 | UINT WinSpyDlg_TimerHandler(UINT_PTR uTimerId)
275 | {
276 | if (uTimerId == 0)
277 | {
278 | DisplayWindowInfo(g_hCurWnd);
279 | return TRUE;
280 | }
281 |
282 | return FALSE;
283 | }
284 |
285 | void ShowAboutDlg(HWND hwndParent)
286 | {
287 | CHAR szText[400];
288 | CHAR szTitle[60];
289 | WCHAR szVersion[40];
290 | WCHAR szCurExe[MAX_PATH];
291 |
292 | GetModuleFileName(0, szCurExe, MAX_PATH);
293 | GetVersionString(szCurExe, TEXT("FileVersion"), szVersion, 40);
294 |
295 | sprintf_s(szText, ARRAYSIZE(szText),
296 | "%S v%S\n"
297 | "\n"
298 | "Original version:\n"
299 | " Copyright(c) 2002 - 2012 by Catch22 Productions\n"
300 | " Written by J Brown\n"
301 | " www.catch22.net | github.com/strobejb/winspy\n"
302 | "\n"
303 | "Forked and improved by various contributors:\n"
304 | " github.com/m417z/winspy\n"
305 | "\n"
306 | "Build details:\n"
307 | " Time: " __DATE__ " " __TIME__
308 | #ifdef WINSPY_GITHUB_FORK
309 | "\n Repository: " STRINGIZE(WINSPY_GITHUB_FORK)
310 | #endif
311 | #ifdef WINSPY_GITHUB_COMMIT
312 | "\n Commit: " STRINGIZE(WINSPY_GITHUB_COMMIT)
313 | #endif
314 | "",
315 | szAppName, szVersion);
316 |
317 | sprintf_s(szTitle, ARRAYSIZE(szTitle), "About %S", szAppName);
318 |
319 | MessageBoxA(hwndParent, szText, szTitle, MB_OK | MB_ICONINFORMATION);
320 | }
321 |
--------------------------------------------------------------------------------
/src/DisplayGeneralInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayGeneralInfo.c
3 | // Copyright (c) 2002 by J Brown
4 | // Freeware
5 | //
6 | // Fill the general-tab-pane with general info for the
7 | // specified window
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | #include "resource.h"
13 | #include "Utils.h"
14 |
15 | void RemoveHyperlink(HWND hwnd, UINT staticid);
16 | void MakeHyperlink(HWND hwnd, UINT staticid, COLORREF crLink);
17 | void FillBytesList(
18 | HWND hwndDlg,
19 | HWND hwnd,
20 | int numBytes,
21 | WORD WINAPI pGetWord(HWND, int),
22 | LONG WINAPI pGetLong(HWND, int),
23 | LONG_PTR WINAPI pGetLongPtr(HWND, int)
24 | );
25 |
26 | //
27 | // Three possible states:
28 | //
29 | // 1. The wndproc isn't known and we have not yet tried to get it.
30 | // The "N/A" link control is shown.
31 | //
32 | // 2. We tried to fetch the wndproc via thread injection and failed.
33 | // The non-link control is shown with a value of "N/A".
34 | //
35 | // 3. We know the wndproc.
36 | // Show the non-link control with the value of the wndproc.
37 | //
38 |
39 | void UpdateWndProcControls(HWND hwnd, HWND hwndDlg, PVOID clsproc)
40 | {
41 | WCHAR ach[100];
42 |
43 | // If we don't know the wndproc and have not already attempted the
44 | // remote thread injection, then show the link.
45 |
46 | BOOL fShowLink = (!g_WndProc && !g_fTriedRemote);
47 |
48 | ShowDlgItem(hwndDlg, IDC_WNDPROC_LINK, fShowLink ? SW_SHOW : SW_HIDE);
49 | ShowDlgItem(hwndDlg, IDC_WNDPROC, fShowLink ? SW_HIDE : SW_SHOW);
50 |
51 | if (g_WndProc == 0)
52 | {
53 | swprintf_s(ach, ARRAYSIZE(ach), L"N/A");
54 | }
55 | else
56 | {
57 | swprintf_s(ach, ARRAYSIZE(ach), L"%p", g_WndProc);
58 |
59 | if (clsproc == NULL)
60 | {
61 | clsproc = (PVOID)(IsWindowUnicode(hwnd) ? GetClassLongPtrW : GetClassLongPtrA)(hwnd, GCLP_WNDPROC);
62 | }
63 |
64 | if (clsproc && (g_WndProc != clsproc))
65 | {
66 | wcscat_s(ach, ARRAYSIZE(ach), L" (Subclassed)");
67 | }
68 | }
69 |
70 | SetDlgItemTextEx(hwndDlg, IDC_WNDPROC_LINK, ach);
71 | SetDlgItemTextEx(hwndDlg, IDC_WNDPROC, ach);
72 | }
73 |
74 |
75 | //
76 | // Clears all the controls on the tab (except the handle value) because either
77 | // there is no current window, or the current window is invalid.
78 | //
79 |
80 | void ResetGeneralTab(HWND hwnd, HWND hwndDlg)
81 | {
82 | // Reset the labels to blank or '(invalid window)'
83 |
84 | PCWSTR pszMessage = hwnd ? szInvalidWindow : L"";
85 |
86 | SetDlgItemTextEx(hwndDlg, IDC_CAPTION1, pszMessage);
87 | SetDlgItemTextEx(hwndDlg, IDC_CAPTION2, pszMessage);
88 | SetDlgItemTextEx(hwndDlg, IDC_CLASS, pszMessage);
89 | SetDlgItemTextEx(hwndDlg, IDC_STYLE, pszMessage);
90 | SetDlgItemTextEx(hwndDlg, IDC_RECTANGLE, pszMessage);
91 | SetDlgItemTextEx(hwndDlg, IDC_CLIENTRECT, pszMessage);
92 | SetDlgItemTextEx(hwndDlg, IDC_WNDPROC_LINK, pszMessage);
93 | SetDlgItemTextEx(hwndDlg, IDC_WNDPROC, pszMessage);
94 | SetDlgItemTextEx(hwndDlg, IDC_INSTANCE, pszMessage);
95 | SetDlgItemTextEx(hwndDlg, IDC_USERDATA, pszMessage);
96 | SetDlgItemTextEx(hwndDlg, IDC_CONTROLID, pszMessage);
97 |
98 | // Reset controls to default states.
99 |
100 | ShowDlgItem(hwndDlg, IDC_CAPTION1, SW_SHOW);
101 | ShowDlgItem(hwndDlg, IDC_CAPTION2, SW_HIDE);
102 | SendDlgItemMessage(hwndDlg, IDC_CAPTION2, CB_RESETCONTENT, 0, 0);
103 |
104 | ShowDlgItem(hwndDlg, IDC_WNDPROC_LINK, SW_HIDE);
105 | ShowDlgItem(hwndDlg, IDC_WNDPROC, SW_SHOW);
106 |
107 | SendDlgItemMessage(hwndDlg, IDC_BYTESLIST, CB_RESETCONTENT, 0, 0);
108 | EnableDlgItem(hwndDlg, IDC_BYTESLIST, FALSE);
109 | }
110 |
111 |
112 | void UpdateGeneralTab(HWND hwnd)
113 | {
114 | WCHAR ach[256];
115 | HWND hwndDlg = WinSpyTab[GENERAL_TAB].hwnd;
116 | RECT rect;
117 |
118 | *ach = 0;
119 | ZeroMemory(&rect, sizeof(rect));
120 |
121 | // Handle
122 |
123 | if (hwnd)
124 | {
125 | swprintf_s(ach, ARRAYSIZE(ach), L"%08X", (UINT)(UINT_PTR)hwnd);
126 | }
127 |
128 | SetDlgItemTextEx(hwndDlg, IDC_HANDLE, ach);
129 |
130 | // This must come after filling in the handle.
131 |
132 | if (!hwnd || !IsWindow(hwnd))
133 | {
134 | ResetGeneralTab(hwnd, hwndDlg);
135 | return;
136 | }
137 |
138 | // Caption
139 | ShowDlgItem(hwndDlg, IDC_CAPTION1, SW_SHOW);
140 | ShowDlgItem(hwndDlg, IDC_CAPTION2, SW_HIDE);
141 |
142 | SendDlgItemMessage(hwndDlg, IDC_CAPTION2, CB_RESETCONTENT, 0, 0);
143 |
144 | // SendMessage is better than GetWindowText,
145 | // because it gets text of children in other processes
146 | if (g_fPassword)
147 | {
148 | // For password edit controls, we try thread injection.
149 |
150 | GetRemoteInfo();
151 | wcscpy_s(ach, ARRAYSIZE(ach), g_szPassword);
152 | }
153 | else
154 | {
155 | ach[0] = 0;
156 |
157 | if (!SendMessageTimeout(hwnd, WM_GETTEXT, ARRAYSIZE(ach), (LPARAM)ach,
158 | SMTO_ABORTIFHUNG, 100, NULL))
159 | {
160 | GetWindowText(hwnd, ach, ARRAYSIZE(ach));
161 | }
162 |
163 | // WM_GETTEXT does not guarantee null termination.
164 |
165 | ach[ARRAYSIZE(ach) - 1] = '\0';
166 | }
167 |
168 | SetDlgItemTextEx(hwndDlg, IDC_CAPTION1, ach); // edit box
169 | SetDlgItemTextEx(hwndDlg, IDC_CAPTION2, ach); // combo box
170 |
171 | // Class name
172 |
173 | GetClassName(hwnd, ach, ARRAYSIZE(ach));
174 | VerboseClassName(ach, ARRAYSIZE(ach), (WORD)GetClassLong(hwnd, GCW_ATOM));
175 |
176 | if (IsWindowUnicode(hwnd))
177 | {
178 | wcscat_s(ach, ARRAYSIZE(ach), L" (Unicode)");
179 | }
180 |
181 | SetDlgItemTextEx(hwndDlg, IDC_CLASS, ach);
182 |
183 | // Style
184 |
185 | swprintf_s(ach, ARRAYSIZE(ach), L"%08X", GetWindowLong(hwnd, GWL_STYLE));
186 | wcscat_s(ach, ARRAYSIZE(ach), IsWindowVisible(hwnd) ? L" (visible, " : L" (hidden, ");
187 | wcscat_s(ach, ARRAYSIZE(ach), IsWindowEnabled(hwnd) ? L"enabled" : L"disabled");
188 |
189 | DWORD dwCloaked = 0;
190 | DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &dwCloaked, sizeof(dwCloaked));
191 |
192 | if (dwCloaked & DWM_CLOAKED_APP)
193 | {
194 | wcscat_s(ach, ARRAYSIZE(ach), L", app cloaked");
195 | }
196 | else if (dwCloaked & DWM_CLOAKED_SHELL)
197 | {
198 | wcscat_s(ach, ARRAYSIZE(ach), L", cloaked");
199 | }
200 |
201 | wcscat_s(ach, ARRAYSIZE(ach), L")");
202 |
203 | SetDlgItemTextEx(hwndDlg, IDC_STYLE, ach);
204 |
205 | // Window rect
206 |
207 | GetWindowRect(hwnd, &rect);
208 | int x1 = rect.left;
209 | int y1 = rect.top;
210 |
211 | FormatDlgItemText(
212 | hwndDlg, IDC_RECTANGLE,
213 | L"(%d,%d) - (%d,%d) - %dx%d",
214 | rect.left, rect.top, rect.right, rect.bottom,
215 | GetRectWidth(&rect), GetRectHeight(&rect));
216 |
217 | // Client rect
218 |
219 | RECT rcClient;
220 |
221 | GetClientRect(hwnd, &rcClient);
222 | MapWindowPoints(hwnd, 0, (POINT *)&rcClient, 2);
223 |
224 | if (!g_fShowClientRectAsMargins)
225 | {
226 | x1 = rcClient.left - x1;
227 | y1 = rcClient.top - y1;
228 |
229 | OffsetRect(&rcClient, -rcClient.left, -rcClient.top);
230 | OffsetRect(&rcClient, x1, y1);
231 |
232 | swprintf_s(ach, ARRAYSIZE(ach), L"(%d,%d) - (%d,%d) - %dx%d",
233 | rcClient.left, rcClient.top, rcClient.right, rcClient.bottom,
234 | GetRectWidth(&rcClient), GetRectHeight(&rcClient));
235 | }
236 | else
237 | {
238 | swprintf_s(ach, ARRAYSIZE(ach), L"{ %d, %d, %d, %d }",
239 | rcClient.left - rect.left,
240 | rcClient.top - rect.top,
241 | rect.right - rcClient.right,
242 | rect.bottom - rcClient.bottom);
243 | }
244 |
245 | SetDlgItemTextEx(hwndDlg, IDC_CLIENTRECT, ach);
246 |
247 | //restored rect
248 | /*GetWindowPlacement(hwnd, &wp);
249 | wsprintf(ach, L"(%d,%d) - (%d,%d) - %dx%d",
250 | wp.rcNormalPosition.left, wp.rcNormalPosition.top,
251 | wp.rcNormalPosition.right, wp.rcNormalPosition.bottom,
252 | (wp.rcNormalPosition.right-wp.rcNormalPosition.left),
253 | (wp.rcNormalPosition.bottom-wp.rcNormalPosition.top));
254 |
255 | SetDlgItemText(hwndDlg, IDC_RESTOREDRECT, ach);*/
256 |
257 | // Window procedure
258 |
259 | UpdateWndProcControls(hwnd, hwndDlg, NULL);
260 |
261 | // Instance handle
262 |
263 | LONG_PTR lp = GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
264 |
265 | FormatDlgItemText(hwndDlg, IDC_INSTANCE, L"%p", (void*)lp);
266 |
267 | // User data
268 |
269 | lp = GetWindowLongPtr(hwnd, GWLP_USERDATA);
270 |
271 | FormatDlgItemText(hwndDlg, IDC_USERDATA, L"%p", (void*)lp);
272 |
273 | // Control ID
274 | //
275 | // despite the name "GWLP_ID" suggesting that control ID is pointer-sized,
276 | // it would only work properly in WM_COMMAND if it was a WORD,
277 | // as it is passed in LOWORD(wParam)
278 |
279 | lp = GetWindowLongPtr(hwnd, GWLP_ID);
280 |
281 | FormatDlgItemText(hwndDlg, IDC_CONTROLID, L"%04IX (%Id)", lp, lp);
282 |
283 | // Extra window bytes
284 |
285 | int numbytes = GetClassLong(hwnd, GCL_CBWNDEXTRA);
286 |
287 | FillBytesList(hwndDlg, hwnd, numbytes, GetWindowWord, GetWindowLong, GetWindowLongPtr);
288 | }
289 |
--------------------------------------------------------------------------------
/src/resource/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by WinSpy.rc
4 | //
5 | #define IDD_TAB_GENERAL 101
6 | #define IDB_DRAGTOOL1 103
7 | #define IDC_CURSOR1 104
8 | #define IDI_APP 105
9 | #define IDD_TAB_STYLES 106
10 | #define IDD_TAB_PROPERTIES 107
11 | #define IDD_TAB_CLASS 108
12 | #define IDD_TAB_WINDOWS 109
13 | #define IDD_TAB_SCROLLBARS 110
14 | #define IDD_MAIN 111
15 | #define IDB_WINDOW_INVISIBLE 113
16 | #define IDB_DRAGTOOL2 116
17 | #define IDB_WINDOW_VISIBLE 117
18 | #define IDD_STYLE_EDIT 118
19 | #define IDI_DOTS 119
20 | #define IDD_OPTIONS 121
21 | #define IDI_DOWN_ARROW 125
22 | #define IDB_CHECK1 126
23 | #define IDB_CHECK2 127
24 | #define IDI_ENTER 135
25 | #define IDD_ADJUSTWINPOS 136
26 | #define IDR_MENU_WINDOW_ONTAB 137
27 | #define IDI_MORE 142
28 | #define IDI_LESS 143
29 | #define IDR_ACCELERATOR1 144
30 | #define IDD_TAB_PROCESS 145
31 | #define IDR_MENU_PROCESS 146
32 | #define IDR_MENU_WINDOW_INTREE 149
33 | #define IDB_PIN_BITMAP 153
34 | #define IDB_SELBOX 162
35 | #define IDR_MENU_PROPERTY 163
36 | #define IDD_PROPERTY_EDIT 164
37 | #define IDR_MENU_BYTES 165
38 | #define IDD_POSTER 166
39 | #define IDD_TAB_DPI 167
40 | #define IDB_WINDOW_CLOAKED 168
41 | #define IDC_LIST1 1000
42 | #define IDC_DRAGGER 1001
43 | #define IDC_LIST2 1001
44 | #define IDC_HANDLE 1002
45 | #define IDC_CAPTION2 1003
46 | #define IDC_WINDOWBYTES 1004
47 | #define IDC_MINIMIZE 1005
48 | #define IDC_CLASS 1006
49 | #define IDC_HIDDEN 1006
50 | #define IDC_STYLE 1007
51 | #define IDC_RECTANGLE 1008
52 | #define IDC_CLIENTRECT 1010
53 | #define IDC_WNDPROC 1011
54 | #define IDC_INSTANCE 1012
55 | #define IDC_CONTROLID 1013
56 | #define IDC_USERDATA 1014
57 | #define IDC_EDIT1 1015
58 | #define IDC_CLASSNAME 1015
59 | #define IDC_EDITY 1016
60 | #define IDC_STYLELIST 1017
61 | #define IDC_EDITW 1017
62 | #define IDC_PID 1017
63 | #define IDC_BYTESLIST 1018
64 | #define IDC_EDITH 1018
65 | #define IDC_TID 1018
66 | #define IDC_TAB1 1019
67 | #define IDC_CLASSBYTES 1020
68 | #define IDC_ATOM 1021
69 | #define IDC_MENUHANDLE 1022
70 | #define IDC_ICONHANDLE 1023
71 | #define IDC_CURSORHANDLE 1024
72 | #define IDC_BKGNDBRUSH 1025
73 | #define IDC_INSTANCEHANDLE 1026
74 | #define IDC_WNDPROC_LINK 1027
75 | #define IDC_CLASSPROC 1028
76 | #define IDC_PARENT 1029
77 | #define IDC_OWNER 1030
78 | #define IDC_HSTATE 1032
79 | #define IDC_HMIN 1033
80 | #define IDC_HMAX 1034
81 | #define IDC_HPOS 1035
82 | #define IDC_HPAGE 1036
83 | #define IDC_CAPTURE 1037
84 | #define IDC_TREE1 1038
85 | #define IDC_STYLEEX 1040
86 | #define IDC_TAB2 1042
87 | #define IDC_LOCATE 1044
88 | #define IDC_REFRESH 1045
89 | #define IDC_RESET 1045
90 | #define IDC_PROCESS_MENU 1045
91 | #define IDC_FLASH 1046
92 | #define IDC_VSTATE 1047
93 | #define IDC_EDITSTYLE 1047
94 | #define IDC_VMIN 1048
95 | #define IDC_EDITSTYLEEX 1048
96 | #define IDC_VMAX 1049
97 | #define IDC_VPOS 1050
98 | #define IDC_VPAGE 1051
99 | #define IDC_CHECK1 1051
100 | #define IDC_OPTIONS_SAVEPOS 1051
101 | #define IDC_OPTIONS_SHOWINCAPTION 1054
102 | #define IDC_OPTIONS_SHOWHIDDEN 1057
103 | #define IDC_OPTIONS_INCHANDLE 1059
104 | #define IDC_OPTIONS_INCCLASS 1060
105 | #define IDC_EDITSIZE 1061
106 | #define IDC_OPTIONS_FULLDRAG 1061
107 | #define IDC_SETCAPTION 1062
108 | #define IDC_ADJUST 1062
109 | #define IDC_OPTIONS_DIR 1062
110 | #define IDC_EDITX 1063
111 | #define IDC_OPTIONS_TOOLTIPS 1063
112 | #define IDC_SPINX 1064
113 | #define IDC_OPTIONS_DESKTOPROOT 1064
114 | #define IDC_SPINY 1065
115 | #define IDC_APPLY 1065
116 | #define IDC_OPTIONS_ENABLE_HOTKEY 1065
117 | #define IDC_SPINW 1066
118 | #define IDC_CAPTION1 1066
119 | #define IDC_OPTIONS_LIST_SHOWHIDDEN 1066
120 | #define IDC_SPINH 1067
121 | #define IDC_PROCESSNAME 1067
122 | #define IDC_OPTIONS_WIN_LABEL 1067
123 | #define IDC_PROCESSPATH 1068
124 | #define IDC_HANDLE_MENU 1069
125 | #define IDC_EXPAND 1070
126 | #define IDC_CLEAR 1071
127 | #define IDC_AUTOUPDATE 1072
128 | #define IDC_RADIO_NAME 1073
129 | #define IDC_RADIO_ATOM 1074
130 | #define IDC_EDIT_NAME 1075
131 | #define IDC_EDIT_HANDLE 1076
132 | #define IDC_POSTER_HANDLE 1077
133 | #define IDC_POSTER_MESSAGES 1078
134 | #define IDC_POSTER_WPARAM 1079
135 | #define IDC_POSTER_LPARAM 1080
136 | #define IDC_POSTER_SEND 1081
137 | #define IDC_POSTER_POST 1082
138 | #define IDC_POSTER_RESULT 1083
139 | #define IDC_PROCESS_DPI_AWARENESS 1084
140 | #define IDC_WINDOW_DPI_AWARENESS 1085
141 | #define IDC_WINDOW_DPI 1086
142 | #define IDC_PROCESS_SYSTEM_DPI_LABEL 1087
143 | #define IDC_PROCESS_SYSTEM_DPI 1088
144 | #define IDC_CLIENTRECT_LABEL 1089
145 | #define IDC_HOTKEY 1089
146 | #define IDC_STYLEEXT_LABEL 1090
147 | #define IDC_STYLEEXT 1091
148 | #define IDC_EDITSTYLEEXT 1092
149 | #define IDC_WINDOW_BAND 1093
150 | #define IDC_ADJUSTWINPOS_ALIGN_TOP 1094
151 | #define IDC_ADJUSTWINPOS_ALIGN_LEFT 1095
152 | #define IDC_ADJUSTWINPOS_ALIGN_CENTER 1096
153 | #define IDC_ADJUSTWINPOS_ALIGN_RIGHT 1097
154 | #define IDC_ADJUSTWINPOS_ALIGN_BOTTOM 1098
155 | #define IDM_GOTO_TAB_GENERAL 3001
156 | #define IDM_GOTO_TAB_STYLES 3002
157 | #define IDM_GOTO_TAB_PROPERTIES 3003
158 | #define IDM_GOTO_TAB_CLASS 3004
159 | #define IDM_GOTO_TAB_WINDOWS 3005
160 | #define IDM_GOTO_TAB_PROCESS 3006
161 | #define IDM_GOTO_TAB_DPI 3007
162 | #define IDM_WINSPY_REFRESH 40007
163 | #define IDM_WINSPY_TERMINATE 40018
164 | #define IDM_WINSPY_POSTQUIT 40019
165 | #define IDM_POPUP_SETPOS 40020
166 | #define IDM_POPUP_VISIBLE 40023
167 | #define IDM_POPUP_ENABLED 40024
168 | #define IDM_POPUP_ONTOP 40025
169 | #define IDM_POPUP_CLOSE 40026
170 | #define IDM_POPUP_TOFRONT 40027
171 | #define IDM_POPUP_TOBACK 40028
172 | #define IDM_WINSPY_PIN 40029
173 | #define IDM_WINSPY_TOGGLE 40032
174 | #define IDM_WINSPY_TOGGLEEXP 40033
175 | #define IDM_WINSPY_OPTIONS 40034
176 | #define IDM_WINSPY_ONTOP 40035
177 | #define IDM_WINSPY_ZOOMTR 40036
178 | #define IDM_WINSPY_ZOOMTL 40037
179 | #define IDM_WINSPY_ZOOMBR 40038
180 | #define IDM_WINSPY_ZOOMBL 40039
181 | #define IDM_WINSPY_FINDEXE 40040
182 | #define IDM_POPUP_CAPTURE 40041
183 | #define IDM_PROPERTY_ADD 40044
184 | #define IDM_PROPERTY_EDIT 40045
185 | #define IDM_PROPERTY_REMOVE 40046
186 | #define IDM_BYTES_COPY 40047
187 | #define IDM_POPUP_POSTER 40048
188 | #define IDM_WINSPY_BROADCASTER 40049
189 |
190 | // Next default values for new objects
191 | //
192 | #ifdef APSTUDIO_INVOKED
193 | #ifndef APSTUDIO_READONLY_SYMBOLS
194 | #define _APS_NO_MFC 1
195 | #define _APS_NEXT_RESOURCE_VALUE 168
196 | #define _APS_NEXT_COMMAND_VALUE 40050
197 | #define _APS_NEXT_CONTROL_VALUE 1099
198 | #define _APS_NEXT_SYMED_VALUE 101
199 | #endif
200 | #endif
201 |
--------------------------------------------------------------------------------
/src/StyleEdit.c:
--------------------------------------------------------------------------------
1 | //
2 | // StyleEdit.c
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // Implements the Style Editor dialog box
8 | //
9 |
10 | #include "WinSpy.h"
11 | #include "FindTool.h"
12 | #include "resource.h"
13 | #include "Utils.h"
14 |
15 | typedef struct
16 | {
17 | HWND hwndTarget; // what window are we looking at??
18 | UINT flavor; // STYLE_FLAVOR_
19 | DWORD dwStyles; // Initial value
20 | ClassStyleInfo* pClassInfo;
21 | }
22 | StyleEditState;
23 |
24 | static StyleEditState g_state;
25 |
26 |
27 | //
28 | // Define our callback function for the Window Finder Tool
29 | //
30 | UINT CALLBACK StyleEditWndFindProc(HWND hwndTool, UINT uCode, HWND hwnd)
31 | {
32 | HWND hwndDlg;
33 | WCHAR szText[120];
34 |
35 | switch (uCode)
36 | {
37 | case WFN_END:
38 | hwndDlg = GetParent(hwndTool);
39 |
40 | if (GetClassLong(g_state.hwndTarget, GCW_ATOM) == GetClassLong(hwnd, GCW_ATOM))
41 | {
42 | DWORD dwStyle = 0;
43 | BOOL fHasValue = FALSE;
44 |
45 | if (g_state.flavor == STYLE_FLAVOR_REGULAR)
46 | {
47 | dwStyle = GetWindowLong(hwnd, GWL_STYLE);
48 | fHasValue = TRUE;
49 | }
50 | else if (g_state.flavor == STYLE_FLAVOR_EX)
51 | {
52 | dwStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
53 | fHasValue = TRUE;
54 | }
55 | else if (GetWindowExtraStyles(hwnd, g_state.pClassInfo, &dwStyle) == ERROR_SUCCESS)
56 | {
57 | fHasValue = TRUE;
58 | }
59 |
60 | if (fHasValue)
61 | {
62 | FormatDlgItemText(hwndDlg, IDC_EDIT1, L"%08X", dwStyle);
63 | }
64 | }
65 | else
66 | {
67 | swprintf_s(szText, ARRAYSIZE(szText), L"Window %08X\n\nUnable to copy this window's styles, \nbecause it belongs to a different class. ", (UINT)(UINT_PTR)hwnd);
68 | MessageBox(hwndDlg, szText, szAppName, MB_OK | MB_ICONINFORMATION);
69 | }
70 |
71 | break;
72 |
73 | }
74 | return 0;
75 | }
76 |
77 | void ApplyStyle(HWND hwndDlg)
78 | {
79 | DWORD dwStyles = (DWORD)GetDlgItemBaseInt(hwndDlg, IDC_EDIT1, 16);
80 |
81 | if (g_state.flavor == STYLE_FLAVOR_REGULAR)
82 | {
83 | SetWindowLong(g_state.hwndTarget, GWL_STYLE, dwStyles);
84 | }
85 | else if (g_state.flavor == STYLE_FLAVOR_EX)
86 | {
87 | SetWindowLong(g_state.hwndTarget, GWL_EXSTYLE, dwStyles);
88 | }
89 | else
90 | {
91 | LRESULT lr;
92 | DWORD_PTR result;
93 |
94 | lr = SendMessageTimeout(
95 | g_state.hwndTarget,
96 | g_state.pClassInfo->SetMessage,
97 | 0,
98 | dwStyles,
99 | SMTO_BLOCK | SMTO_ERRORONEXIT,
100 | 100, // 1/10 second
101 | &result);
102 |
103 | if (!lr)
104 | {
105 | return;
106 | }
107 | }
108 |
109 | // Force the window to repaint.
110 |
111 | SetWindowPos(
112 | g_state.hwndTarget,
113 | 0, 0, 0, 0, 0,
114 | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME);
115 |
116 | InvalidateRect(g_state.hwndTarget, 0, TRUE);
117 | }
118 |
119 | INT_PTR CALLBACK StyleEditProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
120 | {
121 | HWND hwndList;
122 |
123 | DWORD dwStyles;
124 | WCHAR szText[32];
125 |
126 | int topindex;
127 | int caretindex;
128 |
129 | switch (iMsg)
130 | {
131 | case WM_INITDIALOG:
132 |
133 | FormatDlgItemText(hwnd, IDC_EDIT1, L"%08X", g_state.dwStyles);
134 |
135 | MakeFinderTool(GetDlgItem(hwnd, IDC_DRAGGER), StyleEditWndFindProc);
136 |
137 | return TRUE;
138 |
139 | case WM_CLOSE:
140 | EndDialog(hwnd, 0);
141 | return TRUE;
142 |
143 | case WM_MEASUREITEM:
144 | SetWindowLongPtr(hwnd, DWLP_MSGRESULT, FunkyList_MeasureItem(hwnd, (MEASUREITEMSTRUCT *)lParam));
145 | return TRUE;
146 |
147 | case WM_DRAWITEM:
148 | if (wParam == IDC_LIST1)
149 | {
150 | SetWindowLongPtr(hwnd, DWLP_MSGRESULT, FunkyList_DrawItem(hwnd, (UINT)wParam, (DRAWITEMSTRUCT *)lParam));
151 | return TRUE;
152 | }
153 | else
154 | return FALSE;
155 |
156 | case WM_COMMAND:
157 | switch (LOWORD(wParam))
158 | {
159 | case IDC_EDIT1:
160 | switch (HIWORD(wParam))
161 | {
162 | case EN_CHANGE:
163 | dwStyles = (DWORD)GetDlgItemBaseInt(hwnd, IDC_EDIT1, 16);
164 |
165 | hwndList = GetDlgItem(hwnd, IDC_LIST1);
166 |
167 | topindex = (int)SendMessage(hwndList, LB_GETTOPINDEX, 0, 0);
168 | caretindex = (int)SendMessage(hwndList, LB_GETCARETINDEX, 0, 0);
169 |
170 | FillStyleListForEditing(g_state.hwndTarget, hwndList, g_state.flavor, dwStyles);
171 |
172 | SendMessage(hwndList, LB_SETCARETINDEX, caretindex, 0);
173 | SendMessage(hwndList, LB_SETTOPINDEX, topindex, 0);
174 |
175 | return TRUE;
176 | }
177 |
178 | return FALSE;
179 |
180 | case IDC_APPLY:
181 | ApplyStyle(hwnd);
182 | return TRUE;
183 |
184 | case IDCANCEL:
185 | EndDialog(hwnd, 0);
186 | return TRUE;
187 |
188 | case IDC_CLEAR:
189 | // I don't know why anyone would use that button
190 | SetDlgItemText(hwnd, IDC_EDIT1, L"00000000");
191 | return TRUE;
192 |
193 | }
194 |
195 | switch (HIWORD(wParam))
196 | {
197 | case LBN_SELCHANGE:
198 | if (LOWORD(wParam) == IDC_LIST1)
199 | {
200 | hwndList = GetDlgItem(hwnd, IDC_LIST1);
201 |
202 | dwStyles = (DWORD)GetDlgItemBaseInt(hwnd, IDC_EDIT1, 16);
203 |
204 | int caretidx = (int)SendMessage(hwndList, LB_GETCARETINDEX, 0, 0);
205 | int cursel = (int)SendMessage(hwndList, LB_GETSEL, caretidx, 0);
206 |
207 | StyleLookupEx *pStyle = (StyleLookupEx *)SendMessage(hwndList, LB_GETITEMDATA, caretidx, 0);
208 | if (cursel)
209 | {
210 | // The user has just selected this item. This means this item has a style definition:
211 | // the only one that does not is the "unrecognized bits" item,
212 | // and that one is always selected on every repopulation of the list.
213 |
214 | // If there is a dependency, set the dependency style to be present
215 | dwStyles &= ~(pStyle->dependencyValue | pStyle->dependencyExtraMask);
216 | dwStyles |= pStyle->dependencyValue;
217 | // Now set the style itself to be present
218 | dwStyles &= ~(pStyle->value | pStyle->extraMask);
219 | dwStyles |= pStyle->value;
220 | }
221 | else
222 | {
223 | DWORD style;
224 | if (pStyle)
225 | style = pStyle->value;
226 | else
227 | {
228 | // This is the "unrecognized bits" item
229 | SendMessage(hwndList, LB_GETTEXT, caretidx, (LONG_PTR)szText);
230 | style = (DWORD)_tstrtoib16(szText);
231 | }
232 | dwStyles &= ~style;
233 | }
234 |
235 | FormatDlgItemText(hwnd, IDC_EDIT1, L"%08X", dwStyles);
236 |
237 | return TRUE;
238 | }
239 |
240 | return FALSE;
241 | }
242 |
243 | return FALSE;
244 | }
245 | return FALSE;
246 | }
247 |
248 |
249 | void ShowWindowStyleEditor(HWND hwndParent, HWND hwndTarget, UINT flavor)
250 | {
251 | g_state.hwndTarget = hwndTarget;
252 | g_state.flavor = flavor;
253 | g_state.pClassInfo = FindClassStyleInfo(hwndTarget);
254 |
255 | // Fetch the initial value.
256 |
257 | if (flavor == STYLE_FLAVOR_REGULAR)
258 | {
259 | g_state.dwStyles = GetWindowLong(hwndTarget, GWL_STYLE);
260 | }
261 | else if (flavor == STYLE_FLAVOR_EX)
262 | {
263 | g_state.dwStyles = GetWindowLong(hwndTarget, GWL_EXSTYLE);
264 | }
265 | else if (!g_state.pClassInfo || !g_state.pClassInfo->StylesExtra)
266 | {
267 | // Invoked for STYLE_FLAVOR_EXTRA, but we didn't find any class info.
268 | //
269 | // This could happen if the HWND was destroyed out from under us,
270 | // just give up and do nothing.
271 |
272 | return;
273 | }
274 | else
275 | {
276 | // This is STYLE_FLAVOR_EXTRA. If we are unable to query the value
277 | // then don't show the editing dialog.
278 |
279 | DWORD dwErr = GetWindowExtraStyles(
280 | hwndTarget,
281 | g_state.pClassInfo,
282 | &g_state.dwStyles);
283 |
284 | if (dwErr != ERROR_SUCCESS)
285 | {
286 | return;
287 | }
288 | }
289 |
290 | DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_STYLE_EDIT), hwndParent, StyleEditProc, 0);
291 |
292 | // Update the main display
293 | UpdateGeneralTab(hwndTarget);
294 | UpdateStyleTab(hwndTarget);
295 | }
296 |
--------------------------------------------------------------------------------
/src/CaptureWindow.c:
--------------------------------------------------------------------------------
1 | //
2 | // CaptureWindow.c
3 | // Copyright (c) 2002 by J Brown.
4 | // Portions Copyright (c) Microsoft Corporation. (from MSDN)
5 | // Freeware
6 | //
7 | // void CaptureWindow(HWND hwndOwner, HWND hwnd)
8 | //
9 | // hwndOwner - handle to window that owns clipboard (in THIS process)
10 | // hwnd - handle to any window to capture to clipboard
11 | //
12 | // Two bitmaps objects will be placed on the clipboard
13 | // (1x DIB and 1x DDB)
14 | //
15 |
16 | #include "WinSpy.h"
17 | #include "CaptureWindow.h"
18 |
19 | //
20 | // Define this to include DIB support. (Adds to code size)
21 | // Without this constant, only DDB bitmap will be added.
22 | //
23 | #define SUPPORT_DIBS
24 |
25 | #ifdef SUPPORT_DIBS
26 |
27 | #define PALVERSION 0x300
28 |
29 | /* DIB macros */
30 | #define IS_WIN30_DIB(lpbi) ((*(PDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER))
31 | #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
32 |
33 | static WORD PalEntriesOnDevice(HDC hdc)
34 | {
35 | int nColors;
36 |
37 | // Find number of palette entries on this device
38 | nColors = GetDeviceCaps(hdc, SIZEPALETTE);
39 |
40 | if (nColors <= 0 || nColors > MAXWORD)
41 | nColors = GetDeviceCaps(hdc, NUMCOLORS);
42 |
43 | // if nColors is still invalid, force a benign value of 1
44 | if (nColors <= 0 || nColors > MAXWORD)
45 | nColors = 1;
46 |
47 | return (WORD)nColors;
48 | }
49 |
50 | static HPALETTE GetSystemPalette(HDC hdc)
51 | {
52 | static HPALETTE hPal = 0; // handle to a palette
53 | HANDLE hLogPal; // handle to a logical palette
54 | LOGPALETTE *pLogPal; // pointer to a logical palette
55 | WORD nColors; // number of colors
56 |
57 | // Find out how many palette entries we want.
58 | nColors = PalEntriesOnDevice(hdc);
59 |
60 | // Allocate room for the palette and lock it.
61 | hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY));
62 |
63 | // if we didn't get a logical palette, return NULL
64 | if (!hLogPal) return NULL;
65 |
66 | // get a pointer to the logical palette
67 | pLogPal = (LPLOGPALETTE)GlobalLock(hLogPal);
68 |
69 | // set some important fields
70 | pLogPal->palVersion = PALVERSION;
71 | pLogPal->palNumEntries = nColors;
72 |
73 | //Copy the current system palette into our logical palette */
74 | GetSystemPaletteEntries(hdc, 0, nColors, (LPPALETTEENTRY)(pLogPal->palPalEntry));
75 |
76 | // Go ahead and create the palette. Once it's created,
77 | hPal = CreatePalette(pLogPal);
78 |
79 | // clean up
80 | GlobalUnlock(hLogPal);
81 | GlobalFree(hLogPal);
82 |
83 | return hPal;
84 | }
85 |
86 | static WORD DIBNumColors(PSTR lpDIB)
87 | {
88 | WORD wBitCount; // DIB bit count
89 |
90 | if (IS_WIN30_DIB(lpDIB))
91 | {
92 | DWORD dwClrUsed;
93 |
94 | dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed;
95 | if (dwClrUsed)
96 | return (WORD)dwClrUsed;
97 | }
98 |
99 | // Calculate the number of colors in the color table based on
100 | // the number of bits per pixel for the DIB.
101 | //
102 | if (IS_WIN30_DIB(lpDIB))
103 | wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
104 | else
105 | wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount;
106 |
107 | // return number of colors based on bits per pixel
108 | switch (wBitCount)
109 | {
110 | case 1: return 2;
111 | case 4: return 16;
112 | case 8: return 256;
113 | default:return 0;
114 | }
115 | }
116 |
117 | static WORD PaletteSize(PSTR lpDIB)
118 | {
119 | /* calculate the size required by the palette */
120 | if (IS_WIN30_DIB(lpDIB))
121 | return (DIBNumColors(lpDIB) * sizeof(RGBQUAD));
122 | else
123 | return (DIBNumColors(lpDIB) * sizeof(RGBTRIPLE));
124 | }
125 |
126 | static HANDLE BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal)
127 | {
128 | BITMAP bm; // bitmap structure
129 | BITMAPINFOHEADER bi; // bitmap header
130 | BITMAPINFOHEADER *lpbi; // pointer to BITMAPINFOHEADER
131 | DWORD dwLen; // size of memory block
132 | HANDLE hDIB, h; // handle to DIB, temp handle
133 | HDC hDC; // handle to DC
134 | WORD biBits; // bits per pixel
135 |
136 | /* check if bitmap handle is valid */
137 |
138 | if (!hBitmap)
139 | return NULL;
140 |
141 | /* fill in BITMAP structure, return NULL if it didn't work */
142 | if (!GetObject(hBitmap, sizeof(bm), (PSTR)&bm))
143 | return NULL;
144 |
145 | /* if no palette is specified, use default palette */
146 | if (hPal == NULL)
147 | hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
148 |
149 | /* calculate bits per pixel */
150 | biBits = bm.bmPlanes * bm.bmBitsPixel;
151 |
152 | /* make sure bits per pixel is valid */
153 | if (biBits <= 1)
154 | biBits = 1;
155 | else if (biBits <= 4)
156 | biBits = 4;
157 | else if (biBits <= 8)
158 | biBits = 8;
159 | else /* if greater than 8-bit, force to 24-bit */
160 | biBits = 24;
161 |
162 | /* initialize BITMAPINFOHEADER */
163 | bi.biSize = sizeof(BITMAPINFOHEADER);
164 | bi.biWidth = bm.bmWidth;
165 | bi.biHeight = bm.bmHeight;
166 | bi.biPlanes = 1;
167 | bi.biBitCount = biBits;
168 | bi.biCompression = BI_RGB;
169 | bi.biSizeImage = 0;
170 | bi.biXPelsPerMeter = 0;
171 | bi.biYPelsPerMeter = 0;
172 | bi.biClrUsed = 0;
173 | bi.biClrImportant = 0;
174 |
175 | /* calculate size of memory block required to store BITMAPINFO */
176 | dwLen = bi.biSize + PaletteSize((PSTR)&bi);
177 |
178 | /* get a DC */
179 | hDC = GetDC(NULL);
180 |
181 | /* select and realize our palette */
182 | hPal = SelectPalette(hDC, hPal, FALSE);
183 | RealizePalette(hDC);
184 |
185 | /* alloc memory block to store our bitmap */
186 | hDIB = GlobalAlloc(GHND, dwLen);
187 |
188 | /* if we couldn't get memory block */
189 | if (!hDIB)
190 | {
191 | /* clean up and return NULL */
192 | SelectPalette(hDC, hPal, TRUE);
193 | RealizePalette(hDC);
194 | ReleaseDC(NULL, hDC);
195 | return NULL;
196 | }
197 |
198 | /* lock memory and get pointer to it */
199 | lpbi = (BITMAPINFOHEADER *)GlobalLock(hDIB);
200 |
201 | /* use our bitmap info. to fill BITMAPINFOHEADER */
202 | *lpbi = bi;
203 |
204 | /* call GetDIBits with a NULL lpBits param, so it will calculate the
205 | * biSizeImage field for us
206 | */
207 | GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, NULL, (LPBITMAPINFO)lpbi,
208 | DIB_RGB_COLORS);
209 |
210 | /* get the info. returned by GetDIBits and unlock memory block */
211 | bi = *lpbi;
212 | GlobalUnlock(hDIB);
213 |
214 | /* if the driver did not fill in the biSizeImage field, make one up */
215 | if (bi.biSizeImage == 0)
216 | bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight;
217 |
218 | /* realloc the buffer big enough to hold all the bits */
219 | dwLen = bi.biSize + PaletteSize((PSTR)&bi) + bi.biSizeImage;
220 | h = GlobalReAlloc(hDIB, dwLen, 0);
221 | if (h)
222 | hDIB = h;
223 | else
224 | {
225 | /* clean up and return NULL */
226 | GlobalFree(hDIB);
227 | hDIB = NULL;
228 | SelectPalette(hDC, hPal, TRUE);
229 | RealizePalette(hDC);
230 | ReleaseDC(NULL, hDC);
231 | return NULL;
232 | }
233 |
234 | /* lock memory block and get pointer to it */
235 | lpbi = (BITMAPINFOHEADER *)GlobalLock(hDIB);
236 |
237 | /* call GetDIBits with a NON-NULL lpBits param, and actually get the
238 | * bits this time
239 | */
240 | if (GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, (PSTR)lpbi + (WORD)lpbi
241 | ->biSize + PaletteSize((PSTR)lpbi), (LPBITMAPINFO)lpbi,
242 | DIB_RGB_COLORS) == 0)
243 | {
244 | /* clean up and return NULL */
245 | GlobalUnlock(hDIB);
246 | hDIB = NULL;
247 | SelectPalette(hDC, hPal, TRUE);
248 | RealizePalette(hDC);
249 | ReleaseDC(NULL, hDC);
250 | return NULL;
251 | }
252 | bi = *lpbi;
253 |
254 | /* clean up */
255 | GlobalUnlock(hDIB);
256 | SelectPalette(hDC, hPal, TRUE);
257 | RealizePalette(hDC);
258 | ReleaseDC(NULL, hDC);
259 |
260 | /* return handle to the DIB */
261 | return hDIB;
262 | }
263 |
264 | #endif
265 |
266 | BOOL CaptureWindow(HWND hwndOwner, HWND hwnd)
267 | {
268 | RECT rect;
269 | HDC hdc, hdcMem, hdcOld;
270 | HBITMAP hBmp;
271 | HANDLE hDIB;
272 |
273 | HPALETTE hPal;
274 |
275 | int width, height;
276 |
277 | int RasterCapsScrn;
278 | int PaletteSizeScrn;
279 |
280 | GetWindowRect(hwnd, &rect);
281 | width = GetRectWidth(&rect);
282 | height = GetRectHeight(&rect);
283 |
284 | hdc = GetDC(0);
285 |
286 | hdcMem = CreateCompatibleDC(hdc);
287 | hBmp = CreateCompatibleBitmap(hdc, width, height);
288 |
289 | hdcOld = (HDC)SelectObject(hdcMem, hBmp);
290 |
291 | //copy the screen contents
292 | BitBlt(hdcMem, 0, 0, width, height, hdc, rect.left, rect.top, SRCCOPY);
293 | SelectObject(hdcMem, hdcOld);
294 |
295 | OpenClipboard(hwndOwner);
296 | EmptyClipboard();
297 |
298 | #ifdef SUPPORT_DIBS
299 | //palette detection
300 | RasterCapsScrn = GetDeviceCaps(hdc, RASTERCAPS);
301 | PaletteSizeScrn = GetDeviceCaps(hdc, SIZEPALETTE);
302 |
303 | if ((RasterCapsScrn & RC_PALETTE) && (PaletteSizeScrn == 256))
304 | hPal = GetSystemPalette(hdc);
305 | else
306 | hPal = 0;
307 |
308 | hDIB = BitmapToDIB(hBmp, hPal);
309 | SetClipboardData(CF_DIB, hDIB);
310 | #endif
311 |
312 | SetClipboardData(CF_BITMAP, hBmp);
313 |
314 |
315 | CloseClipboard();
316 |
317 | ReleaseDC(0, hdc);
318 |
319 | return TRUE;
320 | }
321 |
--------------------------------------------------------------------------------
/src/FindTool.c:
--------------------------------------------------------------------------------
1 | //
2 | // WinSpy Finder Tool.
3 | //
4 | // Copyright (c) 2002 by J Brown
5 | // Freeware
6 | //
7 | // This is a standalone file which implements
8 | // a "Finder Tool" similar to that used in Spy++
9 | //
10 | // There are two functions you must use:
11 | //
12 | // 1. BOOL MakeFinderTool(HWND hwnd, WNDFINDPROC wfp)
13 | //
14 | // hwnd - handle to a STATIC control to base the tool around.
15 | // MakeFinderTool converts this control to the correct
16 | // style, adds the bitmaps and mouse support etc.
17 | //
18 | // wfn - Event callback function. Must not be zero.
19 | //
20 | // Return values:
21 | // TRUE for success, FALSE for failure
22 | //
23 | //
24 | // 2. UINT CALLBACK WndFindProc(HWND hwndTool, UINT uCode, HWND hwnd)
25 | //
26 | // This is a callback function that you supply when using
27 | // MakeFinderTool. This callback can be executed for a number
28 | // different events - described by uCode.
29 | //
30 | // hwndTool - handle to the finder tool
31 | //
32 | // hwnd - handle to the window which has been found.
33 | //
34 | // uCode - describes the event. Can be one of the following values.
35 | //
36 | // WFN_BEGIN : tool is about to become active.
37 | // WFN_SELCHANGING : sent when tool moves from window-window.
38 | // WFN_SELCHANGED : sent when final window is selected.
39 | // WFN_CANCELLED : Tool cancelled. hwnd is not valid (0)
40 | //
41 | // Return values:
42 | // Return value is only checked for WFN_BEGIN. Return 0 (zero)
43 | // to continue, -1 to prevent tool from being used. Otherwise,
44 | // return 0 (zero) for all other messages
45 | //
46 |
47 | #include "WinSpy.h"
48 |
49 | #include "FindTool.h"
50 | #include "WindowFromPointEx.h"
51 | #include "resource.h"
52 | #include "CaptureWindow.h"
53 |
54 | HWND CreateOverlayWindow(HWND hwndToCover);
55 |
56 | static LONG g_lRefCount = 0;
57 |
58 | //
59 | // Handle to the two dragger bitmaps
60 | //
61 | static HBITMAP hBitmapDrag1, hBitmapDrag2;
62 | static HCURSOR hCursor;
63 |
64 | // Old window procedure...?
65 | static WNDPROC oldstaticproc;
66 |
67 |
68 | //
69 | // These globals are valid while a finder drag operation is active.
70 | //
71 |
72 | static BOOL g_fDragging;
73 | static HWND g_hwndFinder; // The active finder tool window
74 | static HWND g_hwndOverlay; // The overlay/highlight window
75 | static HWND g_hwndCurrent; // The currently selected window
76 | static HWND g_hwndOldFocus; // Who had focus before we took it
77 | static POINT g_ptLast; // Position of last mouse move
78 | static HCURSOR g_hOldCursor;
79 | static BOOL g_fAltDown; // Is the alt key pressed?
80 |
81 |
82 | void LoadFinderResources()
83 | {
84 | hBitmapDrag1 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_DRAGTOOL1));
85 | hBitmapDrag2 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_DRAGTOOL2));
86 |
87 | hCursor = LoadCursor(g_hInst, MAKEINTRESOURCE(IDC_CURSOR1));
88 | }
89 |
90 | void FreeFinderResources()
91 | {
92 | DeleteObject(hBitmapDrag1);
93 | DeleteObject(hBitmapDrag2);
94 |
95 | DestroyCursor(hCursor);
96 | }
97 |
98 | void FindTool_ShowOverlay()
99 | {
100 | g_hwndOverlay = CreateOverlayWindow(g_hwndCurrent);
101 | }
102 |
103 | void FindTool_RemoveOverlay()
104 | {
105 | DestroyWindow(g_hwndOverlay);
106 | g_hwndOverlay = NULL;
107 | }
108 |
109 | UINT FindTool_FireNotify(UINT uCode, HWND hwnd)
110 | {
111 | WNDFINDPROC wfp = (WNDFINDPROC)GetWindowLongPtr(g_hwndFinder, GWLP_USERDATA);
112 | UINT result = 0;
113 |
114 | // Hide the selection overlay during the callout.
115 | if (g_hwndCurrent)
116 | {
117 | FindTool_RemoveOverlay();
118 | }
119 |
120 | if (wfp != 0)
121 | {
122 | result = wfp(g_hwndFinder, uCode, hwnd);
123 | }
124 |
125 | // On selection changed event, the parameter becomes the current selection.
126 | // This means that in the WFN_SELCHANGED case we remove the overlay from
127 | // the old window and show it one the new one.
128 | if (uCode == WFN_SELCHANGED)
129 | {
130 | g_hwndCurrent = hwnd;
131 | }
132 |
133 | // Restore the selection overlay.
134 | // Note that in the WFN_BEGIN, WFN_END, and WFN_CANCELLED cases the current
135 | // window hasn't been set or has already been cleared.
136 | if (g_hwndCurrent)
137 | {
138 | FindTool_ShowOverlay();
139 | }
140 |
141 | return result;
142 | }
143 |
144 | void FindTool_UpdateSelectionFromPoint(POINT pt)
145 | {
146 | HWND hwndPoint;
147 |
148 | ClientToScreen(g_hwndFinder, (POINT *)&pt);
149 |
150 | hwndPoint = WindowFromPointEx(pt, g_fAltDown, g_opts.fShowHidden);
151 |
152 | if (hwndPoint && (hwndPoint != g_hwndCurrent))
153 | {
154 | FindTool_FireNotify(WFN_SELCHANGED, hwndPoint);
155 | }
156 | }
157 |
158 | void FindTool_BeginDrag(HWND hwnd, LPARAM lParam)
159 | {
160 | g_ptLast.x = (short)LOWORD(lParam);
161 | g_ptLast.y = (short)HIWORD(lParam);
162 |
163 | g_hwndFinder = hwnd;
164 |
165 | // Ask the callback function if we want to proceed
166 | if (FindTool_FireNotify(WFN_BEGIN, 0) != -1)
167 | {
168 | g_fDragging = TRUE;
169 | g_fAltDown = ((GetKeyState(VK_MENU) & 0x8000) != 0);
170 |
171 | SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmapDrag2);
172 |
173 | SetCapture(hwnd);
174 | g_hwndOldFocus = SetFocus(hwnd);
175 | g_hOldCursor = SetCursor(hCursor);
176 |
177 | // Select initial window.
178 | g_hwndCurrent = NULL;
179 | FindTool_UpdateSelectionFromPoint(g_ptLast);
180 | }
181 | else
182 | {
183 | g_hwndFinder = NULL;
184 | }
185 | }
186 |
187 | void FindTool_EndDrag(UINT uCode)
188 | {
189 | if (g_fDragging)
190 | {
191 | g_fDragging = FALSE;
192 |
193 | FindTool_RemoveOverlay();
194 | ReleaseCapture();
195 | SetFocus(g_hwndOldFocus);
196 | SetCursor(g_hOldCursor);
197 |
198 | SendMessage(g_hwndFinder, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmapDrag1);
199 |
200 | // Final notification.
201 | HWND hwndForNotify = (uCode == WFN_END) ? g_hwndCurrent : NULL;
202 |
203 | g_hwndCurrent = NULL;
204 |
205 | FindTool_FireNotify(uCode, hwndForNotify);
206 | }
207 | }
208 |
209 | LRESULT CALLBACK StaticProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
210 | {
211 | switch (msg)
212 | {
213 | case WM_LBUTTONDBLCLK:
214 | case WM_LBUTTONDOWN:
215 |
216 | FindTool_BeginDrag(hwnd, lParam);
217 | return 0;
218 |
219 | case WM_MOUSEMOVE:
220 |
221 | if (g_fDragging)
222 | {
223 | POINT pt;
224 |
225 | pt.x = (short)LOWORD(lParam);
226 | pt.y = (short)HIWORD(lParam);
227 |
228 | if (!(g_ptLast.x == pt.x && g_ptLast.y == pt.y))
229 | {
230 | g_ptLast = pt;
231 | FindTool_UpdateSelectionFromPoint(pt);
232 | }
233 | }
234 | return 0;
235 |
236 | case WM_LBUTTONUP:
237 |
238 | // Mouse has been released, so end the find-tool
239 | FindTool_EndDrag(WFN_END);
240 | return 0;
241 |
242 | case WM_GETDLGCODE:
243 | if (wParam == VK_ESCAPE)
244 | {
245 | return DLGC_WANTALLKEYS;
246 | }
247 | break;
248 |
249 | case WM_KEYDOWN:
250 | case WM_KEYUP:
251 | case WM_SYSKEYDOWN:
252 | case WM_SYSKEYUP:
253 |
254 | BOOL newStateReleased = (ULONG)lParam & (1 << 31);
255 | BOOL previousStateDown = (ULONG)lParam & (1 << 30);
256 |
257 | if (wParam == VK_ESCAPE)
258 | {
259 | if (!newStateReleased)
260 | {
261 | FindTool_EndDrag(WFN_CANCELLED);
262 | }
263 | }
264 | else if (wParam == VK_SHIFT)
265 | {
266 | if (newStateReleased)
267 | {
268 | FindTool_FireNotify(WFN_SHIFT_UP, 0);
269 | }
270 | else if (!previousStateDown)
271 | {
272 | FindTool_FireNotify(WFN_SHIFT_DOWN, 0);
273 | }
274 | }
275 | else if (wParam == VK_CONTROL)
276 | {
277 | if (newStateReleased)
278 | {
279 | FindTool_FireNotify(WFN_CTRL_UP, 0);
280 | }
281 | else if (!previousStateDown)
282 | {
283 | FindTool_FireNotify(WFN_CTRL_DOWN, 0);
284 | }
285 | }
286 | else if (wParam == VK_MENU) // Alt Key
287 | {
288 | g_fAltDown = !newStateReleased;
289 | FindTool_UpdateSelectionFromPoint(g_ptLast);
290 | }
291 |
292 | break;
293 |
294 | case WM_KILLFOCUS:
295 | {
296 | FindTool_EndDrag(WFN_CANCELLED);
297 | break;
298 | }
299 |
300 | case WM_NCDESTROY:
301 |
302 | // When the last finder tool has been destroyed, free
303 | // up all the resources
304 | if (InterlockedDecrement(&g_lRefCount) == 0)
305 | {
306 | FreeFinderResources();
307 | }
308 |
309 | break;
310 | }
311 |
312 | return CallWindowProc(oldstaticproc, hwnd, msg, wParam, lParam);
313 | }
314 |
315 |
316 | BOOL MakeFinderTool(HWND hwnd, WNDFINDPROC wfp)
317 | {
318 | DWORD dwStyle;
319 |
320 | // If this is the first finder tool, then load
321 | // the bitmap and mouse-cursor resources
322 | if (InterlockedIncrement(&g_lRefCount) == 1)
323 | {
324 | LoadFinderResources();
325 | }
326 |
327 | // Apply styles to make this a picture control
328 | dwStyle = GetWindowLong(hwnd, GWL_STYLE);
329 |
330 | // Turn OFF styles we don't want
331 | dwStyle &= ~(SS_RIGHT | SS_CENTER | SS_CENTERIMAGE);
332 | dwStyle &= ~(SS_ICON | SS_SIMPLE | SS_LEFTNOWORDWRAP);
333 |
334 | // Turn ON styles we must have
335 | dwStyle |= SS_NOTIFY;
336 | dwStyle |= SS_BITMAP;
337 |
338 | // Now apply them.
339 | SetWindowLong(hwnd, GWL_STYLE, dwStyle);
340 |
341 | // Set the default bitmap
342 | SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmapDrag1);
343 |
344 | // Set the callback for this control
345 | SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)wfp);
346 |
347 | // Subclass the static control
348 | oldstaticproc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)StaticProc);
349 |
350 | return TRUE;
351 | }
352 |
--------------------------------------------------------------------------------
/src/BitmapButton.c:
--------------------------------------------------------------------------------
1 | //
2 | // BitmapButton.c
3 | // Copyright (c) 2002 by J Brown
4 | // Freeware
5 | //
6 | // void MakeBitmapButton(HWND hwnd, UINT uIconId)
7 | //
8 | // Converts the specified button into an owner-drawn button
9 | // (supports a small icon + text to the right)
10 | //
11 | // hwnd - handle to button
12 | // uIconId - icon resource ID (loaded from THIS module)
13 | //
14 | //
15 | // BOOL DrawBitmapButton(DRAWITEMSTRUCT *dis)
16 | //
17 | // You must call this when the parent (dialog?) window receives a
18 | // WM_DRAWITEM for the button.
19 | //
20 |
21 | #include "WinSpy.h"
22 |
23 | #include
24 | #include //
25 | #include "BitmapButton.h"
26 |
27 | BOOL g_fThemeApiAvailable = FALSE;
28 |
29 | HTHEME _OpenThemeData(HWND hwnd, PCWSTR pszClassList)
30 | {
31 | if (g_fThemeApiAvailable)
32 | return OpenThemeData(hwnd, pszClassList);
33 | else
34 | return NULL;
35 | }
36 |
37 | HRESULT _CloseThemeData(HTHEME hTheme)
38 | {
39 | if (g_fThemeApiAvailable)
40 | return CloseThemeData(hTheme);
41 | else
42 | return E_FAIL;
43 | }
44 |
45 | #ifndef ODS_NOFOCUSRECT
46 | #define ODS_NOFOCUSRECT 0x0200
47 | #endif
48 |
49 | #ifndef DT_HIDEPREFIX
50 | #define DT_HIDEPREFIX 0x100000
51 | #endif
52 |
53 |
54 | //
55 | // Subclass procedure for an owner-drawn button.
56 | // All this does is to re-enable double-click behavior for
57 | // an owner-drawn button.
58 | //
59 | static LRESULT CALLBACK BBProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
60 | {
61 | WNDPROC oldproc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
62 |
63 | static BOOL mouseOver;
64 | POINT pt;
65 | RECT rect;
66 |
67 | switch (msg)
68 | {
69 | case WM_LBUTTONDBLCLK:
70 | msg = WM_LBUTTONDOWN;
71 | break;
72 |
73 | case WM_MOUSEMOVE:
74 |
75 | if (!mouseOver)
76 | {
77 | SetTimer(hwnd, 0, 15, 0);
78 | mouseOver = FALSE;
79 | }
80 | break;
81 |
82 | case WM_TIMER:
83 |
84 | GetCursorPos(&pt);
85 | ScreenToClient(hwnd, &pt);
86 | GetClientRect(hwnd, &rect);
87 |
88 | if (PtInRect(&rect, pt))
89 | {
90 | if (!mouseOver)
91 | {
92 | mouseOver = TRUE;
93 | InvalidateRect(hwnd, 0, 0);
94 | }
95 | }
96 | else
97 | {
98 | mouseOver = FALSE;
99 | KillTimer(hwnd, 0);
100 | InvalidateRect(hwnd, 0, 0);
101 | }
102 |
103 | return 0;
104 |
105 | // Under Win2000 / XP, Windows sends a strange message
106 | // to dialog controls, whenever the ALT key is pressed
107 | // for the first time (i.e. to show focus rect / & prefixes etc).
108 | // msg = 0x0128, wParam = 0x00030003, lParam = 0
109 | case 0x0128:
110 | InvalidateRect(hwnd, 0, 0);
111 | break;
112 | }
113 |
114 | return CallWindowProc(oldproc, hwnd, msg, wParam, lParam);
115 | }
116 |
117 | //BOOL DrawThemedBitmapButton(DRAWITEMSTRUCT *dis)
118 | /*BOOL DrawBitmapButton0(DRAWITEMSTRUCT *dis)
119 | {
120 | //HTHEME hTheme = GetWindowTheme(dis->hwndItem, "Button");
121 | HTHEME hTheme = _OpenThemeData(dis->hwndItem, L"Button");
122 | DWORD state;
123 |
124 | if(dis->itemState & ODA_FOCUS)
125 | ;
126 |
127 | if(dis->itemState & ODS_SELECTED)
128 | state = PBS_PRESSED;
129 | else if(dis->itemState & ODS_HOTLIGHT)
130 | state = PBS_HOT;
131 | else
132 | state = PBS_NORMAL;
133 |
134 | DrawThemeBackground(hTheme, dis->hDC, BP_PUSHBUTTON, state, &dis->rcItem, 0);
135 |
136 | _CloseThemeData(hTheme);
137 |
138 | return TRUE;
139 | }*/
140 |
141 | //
142 | // Call this function whenever you get a WM_DRAWITEM in the parent dialog.
143 | //
144 | BOOL DrawBitmapButton(DRAWITEMSTRUCT *dis)
145 | {
146 | RECT rect; // Drawing rectangle
147 | POINT pt;
148 |
149 | int ix, iy; // Icon offset
150 | int bx; // border sizes
151 | int cxIconBorder;
152 | int sxIcon, syIcon; // Icon size
153 | int xoff, yoff; //
154 |
155 | WCHAR szText[100];
156 | size_t nTextLen;
157 |
158 | HICON hIcon;
159 | DWORD dwStyle = GetWindowLong(dis->hwndItem, GWL_STYLE);
160 |
161 | DWORD dwDTflags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
162 | BOOL fRightAlign;
163 |
164 | // XP/Vista theme support
165 | DWORD dwThemeFlags;
166 | HTHEME hTheme;
167 | //BOOL fDrawThemed = g_fThemeApiAvailable;
168 |
169 | if (dis->itemState & ODS_NOFOCUSRECT)
170 | dwDTflags |= DT_HIDEPREFIX;
171 |
172 | fRightAlign = (dwStyle & BS_RIGHT) ? TRUE : FALSE;
173 |
174 | // do the theme thing
175 | hTheme = _OpenThemeData(dis->hwndItem, L"Button");
176 |
177 | switch (dis->itemAction)
178 | {
179 | // We need to redraw the whole button, no
180 | // matter what DRAWITEM event we receive.
181 | case ODA_FOCUS:
182 | case ODA_SELECT:
183 | case ODA_DRAWENTIRE:
184 |
185 | // Retrieve button text
186 | GetWindowText(dis->hwndItem, szText, ARRAYSIZE(szText));
187 |
188 | nTextLen = wcslen(szText);
189 |
190 | // Retrieve button icon
191 | hIcon = (HICON)SendMessage(dis->hwndItem, BM_GETIMAGE, IMAGE_ICON, 0);
192 |
193 | // Find icon dimensions
194 | sxIcon = syIcon = DPIScale(dis->hwndItem, 16);
195 |
196 | CopyRect(&rect, &dis->rcItem);
197 | GetCursorPos(&pt);
198 | ScreenToClient(dis->hwndItem, &pt);
199 |
200 | if (PtInRect(&rect, pt))
201 | dis->itemState |= ODS_HOTLIGHT;
202 |
203 | // border dimensions
204 | bx = DPIScale(dis->hwndItem, 2);
205 | cxIconBorder = DPIScale(dis->hwndItem, 3);
206 |
207 | // icon offsets
208 | if (nTextLen == 0)
209 | {
210 | // center the image if no text
211 | ix = (GetRectWidth(&rect) - sxIcon) / 2;
212 | }
213 | else
214 | {
215 | if (fRightAlign)
216 | ix = rect.right - bx - cxIconBorder - sxIcon;
217 | else
218 | ix = rect.left + bx + cxIconBorder;
219 | }
220 |
221 | // center image vertically
222 | iy = (GetRectHeight(&rect) - syIcon) / 2;
223 |
224 | InflateRect(&rect, -5, -5);
225 |
226 | // Draw a single-line black border around the button
227 | if (hTheme == NULL && (dis->itemState & (ODS_FOCUS | ODS_DEFAULT)))
228 | {
229 | FrameRect(dis->hDC, &dis->rcItem, (HBRUSH)GetStockObject(BLACK_BRUSH));
230 | InflateRect(&dis->rcItem, -1, -1);
231 | }
232 |
233 | if (dis->itemState & ODS_FOCUS)
234 | dwThemeFlags = PBS_DEFAULTED;
235 | if (dis->itemState & ODS_DISABLED)
236 | dwThemeFlags = PBS_DISABLED;
237 | else if (dis->itemState & ODS_SELECTED)
238 | dwThemeFlags = PBS_PRESSED;
239 | else if (dis->itemState & ODS_HOTLIGHT)
240 | dwThemeFlags = PBS_HOT;
241 | else if (dis->itemState & ODS_DEFAULT)
242 | dwThemeFlags = PBS_DEFAULTED;
243 | else
244 | dwThemeFlags = PBS_NORMAL;
245 |
246 | // Button is DOWN
247 | if (dis->itemState & ODS_SELECTED)
248 | {
249 | // Draw a button
250 | if (hTheme != NULL)
251 | DrawThemeBackground(hTheme, dis->hDC, BP_PUSHBUTTON, dwThemeFlags, &dis->rcItem, 0);
252 | else
253 | DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED | DFCS_FLAT);
254 |
255 |
256 | // Offset contents to make it look "pressed"
257 | if (hTheme == NULL)
258 | {
259 | OffsetRect(&rect, 1, 1);
260 | xoff = yoff = 1;
261 | }
262 | else
263 | xoff = yoff = 0;
264 | }
265 | // Button is UP
266 | else
267 | {
268 | //
269 | if (hTheme != NULL)
270 | DrawThemeBackground(hTheme, dis->hDC, BP_PUSHBUTTON, dwThemeFlags, &dis->rcItem, 0);
271 | else
272 | DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
273 |
274 | xoff = yoff = 0;
275 | }
276 |
277 | // Draw the icon
278 | DrawIconEx(dis->hDC, ix + xoff, iy + yoff, hIcon, sxIcon, syIcon, 0, 0, DI_NORMAL);
279 |
280 | // Adjust position of window text
281 | if (fRightAlign)
282 | {
283 | rect.left += bx + cxIconBorder;
284 | rect.right -= sxIcon + bx + cxIconBorder;
285 | }
286 | else
287 | {
288 | rect.right -= bx + cxIconBorder;
289 | rect.left += sxIcon + bx + cxIconBorder;
290 | }
291 |
292 | // Draw the text
293 | OffsetRect(&rect, 0, -1);
294 | SetBkMode(dis->hDC, TRANSPARENT);
295 | DrawText(dis->hDC, szText, -1, &rect, dwDTflags);
296 | OffsetRect(&rect, 0, 1);
297 |
298 | // Draw the focus rectangle
299 | if (dis->itemState & ODS_FOCUS)
300 | {
301 | if (!(dis->itemState & ODS_NOFOCUSRECT))
302 | {
303 | // Get a "fresh" copy of the button rectangle
304 | CopyRect(&rect, &dis->rcItem);
305 |
306 | if (nTextLen > 0)
307 | {
308 | if (fRightAlign)
309 | rect.right -= sxIcon + bx;
310 | else
311 | rect.left += sxIcon + bx + 2;
312 | }
313 |
314 | int cx = 2 + DPIScale(dis->hwndItem, 1);
315 |
316 | InflateRect(&rect, -cx, -cx);
317 |
318 | DrawFocusRect(dis->hDC, &rect);
319 | }
320 | }
321 |
322 | break;
323 | }
324 |
325 | _CloseThemeData(hTheme);
326 | return TRUE;
327 | }
328 |
329 | //
330 | // Convert the specified button into an owner-drawn button.
331 | // The button does NOT need owner-draw or icon styles set
332 | // in the resource editor - this function sets these
333 | // styles automatically
334 | //
335 | void MakeBitmapButton(HWND hwnd, UINT uIconId)
336 | {
337 | WNDPROC oldproc;
338 | DWORD dwStyle;
339 | int cxIcon = DPIScale(hwnd, 16);
340 |
341 | HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(uIconId), IMAGE_ICON, cxIcon, cxIcon, 0);
342 |
343 | // Add on BS_ICON and BS_OWNERDRAW styles
344 | dwStyle = GetWindowLong(hwnd, GWL_STYLE);
345 | SetWindowLong(hwnd, GWL_STYLE, dwStyle | BS_ICON | BS_OWNERDRAW);
346 |
347 | // Assign icon to the button
348 | SendMessage(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
349 |
350 | // Subclass (to reenable double-clicks)
351 | oldproc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)BBProc);
352 |
353 | // Store old procedure
354 | SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
355 |
356 | if (g_fThemeApiAvailable)
357 | SetWindowTheme(hwnd, L"explorer", NULL);
358 | }
359 |
360 | //
361 | // Just a helper function really
362 | //
363 | void MakeDlgBitmapButton(HWND hwndDlg, UINT uCtrlId, UINT uIconId)
364 | {
365 | if (GetModuleHandle(L"uxtheme.dll"))
366 | g_fThemeApiAvailable = TRUE;
367 | else
368 | g_fThemeApiAvailable = FALSE;
369 |
370 | MakeBitmapButton(GetDlgItem(hwndDlg, uCtrlId), uIconId);
371 | }
372 |
--------------------------------------------------------------------------------
/src/winspy.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | ARM
7 |
8 |
9 | Debug
10 | ARM64
11 |
12 |
13 | Debug
14 | Win32
15 |
16 |
17 | Debug
18 | x64
19 |
20 |
21 | Release
22 | ARM
23 |
24 |
25 | Release
26 | ARM64
27 |
28 |
29 | Release
30 | Win32
31 |
32 |
33 | Release
34 | x64
35 |
36 |
37 |
38 | {3E78711E-0602-4FD9-8F79-18EF3D5BA3CD}
39 | Win32Proj
40 | winspy
41 | 10.0
42 | v143
43 |
44 |
45 |
46 | Application
47 | Unicode
48 |
49 |
50 | true
51 |
52 |
53 | false
54 | true
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | $(SolutionDir)bin\$(PlatformShortName)\$(Configuration)\
86 | $(Platform)\$(Configuration)\
87 | true
88 |
89 |
90 | true
91 |
92 |
93 | false
94 |
95 |
96 |
97 | Level4
98 | WIN32;_WINDOWS;%(PreprocessorDefinitions)
99 | true
100 | resource
101 | WinSpy.h
102 | Use
103 | /DWINSPY_GITHUB_FORK="$(WINSPY_GITHUB_FORK)"
104 | /DWINSPY_GITHUB_COMMIT="$(WINSPY_GITHUB_COMMIT)"
105 |
106 |
107 | Windows
108 | uxtheme.dll;windowscodecs.dll
109 | psapi.lib;version.lib;uxtheme.lib;windowscodecs.lib;comctl32.lib;gdi32.lib;Advapi32.lib;Shell32.lib;Ole32.lib;%(AdditionalDependencies);dwmapi.lib
110 | /DEPENDENTLOADFLAG:0x800 %(AdditionalOptions)
111 |
112 |
113 | resource\winspy.exe.manifest
114 |
115 |
116 |
117 |
118 | Disabled
119 | _DEBUG;%(PreprocessorDefinitions)
120 | MultiThreadedDebug
121 |
122 |
123 |
124 |
125 | MinSpace
126 | true
127 | true
128 | NDEBUG;%(PreprocessorDefinitions)
129 | MultiThreaded
130 |
131 |
132 | true
133 | true
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | NotUsing
152 |
153 |
154 |
155 |
156 |
157 | NotUsing
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 | Create
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
--------------------------------------------------------------------------------
/src/DisplayDpiInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayDpiInfo.c
3 | //
4 | // Fills the DPI tab-pane with info for the current window.
5 | //
6 |
7 | #include "WinSpy.h"
8 |
9 | #include "resource.h"
10 | #include "Utils.h"
11 |
12 |
13 | //
14 | // Definitions from the platform SDK
15 | //
16 | #ifndef DPI_AWARENESS_CONTEXT_UNAWARE
17 |
18 | DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
19 |
20 | #define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1)
21 | #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2)
22 | #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3)
23 | #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
24 | #define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5)
25 |
26 | #define DPI_AWARENESS_SYSTEM_AWARE 1
27 |
28 | #endif
29 |
30 |
31 |
32 | //
33 | // These APIs exist on Windows 10 and later only.
34 | //
35 | typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND);
36 | typedef DPI_AWARENESS_CONTEXT (WINAPI * PFN_GetWindowDpiAwarenessContext)(HWND);
37 | typedef BOOL (WINAPI * PFN_AreDpiAwarenessContextsEqual)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT);
38 | typedef BOOL (WINAPI * PFN_GetProcessDpiAwareness)(HANDLE, int *);
39 | typedef DPI_AWARENESS_CONTEXT (WINAPI * PFN_GetDpiAwarenessContextForProcess)(HANDLE);
40 | typedef UINT (WINAPI * PFN_GetSystemDpiForProcess)(HANDLE);
41 | typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
42 | typedef UINT (WINAPI * PFN_GetAwarenessFromDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
43 | typedef BOOL (WINAPI * PFN_GetWindowBand)(HWND hWnd, PDWORD pdwBand);
44 |
45 | static PFN_GetDpiForWindow s_pfnGetDpiForWindow = NULL;
46 | static PFN_GetWindowDpiAwarenessContext s_pfnGetWindowDpiAwarenessContext = NULL;
47 | static PFN_AreDpiAwarenessContextsEqual s_pfnAreDpiAwarenessContextsEqual = NULL;
48 | static PFN_GetProcessDpiAwareness s_pfnGetProcessDpiAwareness = NULL;
49 | static PFN_GetDpiAwarenessContextForProcess s_pfnGetDpiAwarenessContextForProcess = NULL;
50 | static PFN_GetSystemDpiForProcess s_pfnGetSystemDpiForProcess = NULL;
51 | static PFN_SetProcessDpiAwarenessContext s_pfnSetProcessDpiAwarenessContext = NULL;
52 | static PFN_GetAwarenessFromDpiAwarenessContext s_pfnGetAwarenessFromDpiAwarenessContext = NULL;
53 | static PFN_GetWindowBand s_pfnGetWindowBand = NULL;
54 |
55 | static BOOL s_fCheckedForAPIs = FALSE;
56 |
57 | // https://blog.adeltax.com/window-z-order-in-windows-10/
58 | static PCSTR s_pszWindowBands[] = {
59 | "ZBID_DEFAULT",
60 | "ZBID_DESKTOP",
61 | "ZBID_UIACCESS",
62 | "ZBID_IMMERSIVE_IHM",
63 | "ZBID_IMMERSIVE_NOTIFICATION",
64 | "ZBID_IMMERSIVE_APPCHROME",
65 | "ZBID_IMMERSIVE_MOGO",
66 | "ZBID_IMMERSIVE_EDGY",
67 | "ZBID_IMMERSIVE_INACTIVEMOBODY",
68 | "ZBID_IMMERSIVE_INACTIVEDOCK",
69 | "ZBID_IMMERSIVE_ACTIVEMOBODY",
70 | "ZBID_IMMERSIVE_ACTIVEDOCK",
71 | "ZBID_IMMERSIVE_BACKGROUND",
72 | "ZBID_IMMERSIVE_SEARCH",
73 | "ZBID_GENUINE_WINDOWS",
74 | "ZBID_IMMERSIVE_RESTRICTED",
75 | "ZBID_SYSTEM_TOOLS",
76 |
77 | //Windows 10+
78 | "ZBID_LOCK",
79 | "ZBID_ABOVELOCK_UX",
80 | };
81 |
82 | void InitializeDpiApis()
83 | {
84 | if (!s_fCheckedForAPIs)
85 | {
86 | HMODULE hmod = GetModuleHandle(L"user32");
87 |
88 | s_pfnGetDpiForWindow = (PFN_GetDpiForWindow)GetProcAddress(hmod, "GetDpiForWindow");
89 | s_pfnGetWindowDpiAwarenessContext = (PFN_GetWindowDpiAwarenessContext)GetProcAddress(hmod, "GetWindowDpiAwarenessContext");
90 | s_pfnAreDpiAwarenessContextsEqual = (PFN_AreDpiAwarenessContextsEqual)GetProcAddress(hmod, "AreDpiAwarenessContextsEqual");
91 | s_pfnGetProcessDpiAwareness = (PFN_GetProcessDpiAwareness)GetProcAddress(hmod, "GetProcessDpiAwareness");
92 | s_pfnGetDpiAwarenessContextForProcess = (PFN_GetDpiAwarenessContextForProcess)GetProcAddress(hmod, "GetDpiAwarenessContextForProcess");
93 | s_pfnGetSystemDpiForProcess = (PFN_GetSystemDpiForProcess)GetProcAddress(hmod, "GetSystemDpiForProcess");
94 | s_pfnSetProcessDpiAwarenessContext = (PFN_SetProcessDpiAwarenessContext)GetProcAddress(hmod, "SetProcessDpiAwarenessContext");
95 | s_pfnGetAwarenessFromDpiAwarenessContext = (PFN_GetAwarenessFromDpiAwarenessContext)GetProcAddress(hmod, "GetAwarenessFromDpiAwarenessContext");
96 | s_pfnGetWindowBand = (PFN_GetWindowBand)GetProcAddress(hmod, "GetWindowBand");
97 | s_fCheckedForAPIs = TRUE;
98 | }
99 | }
100 |
101 | BOOL IsGetSystemDpiForProcessPresent()
102 | {
103 | InitializeDpiApis();
104 |
105 | return (s_pfnGetSystemDpiForProcess != NULL);
106 | }
107 |
108 | void DescribeDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiContext, PSTR pszBuffer, size_t cchBuffer)
109 | {
110 | PSTR pszValue = NULL;
111 |
112 | if (s_pfnAreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_UNAWARE))
113 | {
114 | pszValue = "Unaware";
115 | }
116 | else if (s_pfnAreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
117 | {
118 | pszValue = "Per-Monitor Aware";
119 | }
120 | else if (s_pfnAreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
121 | {
122 | pszValue = "Per-Monitor Aware v2";
123 | }
124 | else if (s_pfnAreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED))
125 | {
126 | pszValue = "Unaware (GDI Scaled)";
127 | }
128 | else if (s_pfnGetAwarenessFromDpiAwarenessContext(dpiContext) == DPI_AWARENESS_SYSTEM_AWARE)
129 | {
130 | //
131 | // Windows 10 version 1803 (April 2018 Update) introduced a feature
132 | // where system-aware applications/windows can be associated with the
133 | // DPI of the primary monitor at the point in time that the process
134 | // started, rather than the older behavior where system-aware was
135 | // associated with the DPI of the primary monitor when the user
136 | // session started.
137 | //
138 | // This means that the system-aware DPI context from two different
139 | // processes may be associated with different DPI values, and that
140 | // means that we cannot use AreDpiAwarenessContextsEqual to determine
141 | // if the other process is system-aware. Instead we can extract the
142 | // underlying awareness enum from the context and examine it instead.
143 | //
144 |
145 | pszValue = "System Aware";
146 | }
147 |
148 | if (pszValue)
149 | {
150 | StringCchCopyA(pszBuffer, cchBuffer, pszValue);
151 | }
152 | else
153 | {
154 | StringCchPrintfA(pszBuffer, cchBuffer, "Unknown (0x%08p)", dpiContext);
155 | }
156 | }
157 |
158 | void DescribeProcessDpiAwareness(DWORD dwProcessId, PSTR pszAwareness, size_t cchAwareness, PSTR pszDpi, size_t cchDpi)
159 | {
160 | HANDLE hProcess = NULL;
161 |
162 | InitializeDpiApis();
163 |
164 | *pszAwareness = '\0';
165 | *pszDpi = '\0';
166 |
167 | if (s_pfnGetDpiAwarenessContextForProcess || s_pfnGetProcessDpiAwareness)
168 | {
169 | hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId);
170 |
171 | if (!hProcess)
172 | {
173 | DWORD dwError = GetLastError();
174 |
175 | if (dwError == ERROR_ACCESS_DENIED)
176 | {
177 | StringCchCopyA(pszAwareness, cchAwareness, "");
178 | StringCchCopyA(pszDpi, cchDpi, "");
179 | }
180 | }
181 | }
182 | else
183 | {
184 | StringCchCopyA(pszAwareness, cchAwareness, "");
185 | }
186 |
187 | if (hProcess)
188 | {
189 | if (s_pfnGetDpiAwarenessContextForProcess)
190 | {
191 | DPI_AWARENESS_CONTEXT dpiContext = s_pfnGetDpiAwarenessContextForProcess(hProcess);
192 |
193 | if (dpiContext)
194 | {
195 | DescribeDpiAwarenessContext(dpiContext, pszAwareness, cchAwareness);
196 | }
197 | }
198 | else if (s_pfnGetProcessDpiAwareness)
199 | {
200 | CHAR szValue[MAX_PATH] = "?";
201 | PCSTR pszValue = szValue;
202 | int dwLevel;
203 |
204 | if (s_pfnGetProcessDpiAwareness(hProcess, &dwLevel))
205 | {
206 | switch (dwLevel)
207 | {
208 | case 0: // PROCESS_DPI_UNAWARE
209 | pszValue = "Unaware";
210 | break;
211 |
212 | case 1: // PROCESS_SYSTEM_DPI_AWARE
213 | pszValue = "System Aware";
214 | break;
215 |
216 | case 2: // PROCESS_PER_MONITOR_DPI_AWARE
217 | pszValue = "Per-Monitor Aware";
218 | break;
219 |
220 | default:
221 | sprintf_s(szValue, ARRAYSIZE(szValue), "Unknown (%d)", dwLevel);
222 | break;
223 | }
224 | }
225 |
226 | StringCchCopyA(pszAwareness, cchAwareness, pszValue);
227 | }
228 |
229 | if (s_pfnGetSystemDpiForProcess)
230 | {
231 | UINT dpi = s_pfnGetSystemDpiForProcess(hProcess);
232 | UINT percent = (UINT)(dpi * 100 / 96);
233 |
234 | if (dpi)
235 | {
236 | sprintf_s(pszDpi, cchDpi, "%d (%u%%)", dpi, percent);
237 | }
238 | else
239 | {
240 | StringCchCopyA(pszDpi, cchDpi, "");
241 | }
242 | }
243 |
244 | CloseHandle(hProcess);
245 | }
246 | }
247 |
248 | //
249 | // Update the DPI tab for the specified window
250 | //
251 | void UpdateDpiTab(HWND hwnd)
252 | {
253 | HWND hwndDlg = WinSpyTab[DPI_TAB].hwnd;
254 | CHAR szTemp[100];
255 | PSTR pszValue = NULL;
256 | BOOL fValid;
257 |
258 | InitializeDpiApis();
259 |
260 | fValid = (hwnd && IsWindow(hwnd));
261 |
262 | if (!fValid)
263 | {
264 | pszValue = (hwnd == NULL) ? "" : "(invalid window)";
265 | }
266 |
267 | // DPI field
268 |
269 | if (fValid)
270 | {
271 | if (s_pfnGetDpiForWindow)
272 | {
273 | UINT dpi = s_pfnGetDpiForWindow(hwnd);
274 | UINT percent = (UINT)(dpi * 100 / 96);
275 |
276 | sprintf_s(szTemp, ARRAYSIZE(szTemp), "%d (%u%%)", dpi, percent);
277 | pszValue = szTemp;
278 | }
279 | else
280 | {
281 | pszValue = "";
282 | }
283 | }
284 |
285 | SetDlgItemTextExA(hwndDlg, IDC_WINDOW_DPI, pszValue);
286 |
287 | // DPI awareness field
288 |
289 | if (fValid)
290 | {
291 | if (s_pfnGetWindowDpiAwarenessContext)
292 | {
293 | DPI_AWARENESS_CONTEXT dpiContext = s_pfnGetWindowDpiAwarenessContext(hwnd);
294 |
295 | DescribeDpiAwarenessContext(dpiContext, szTemp, ARRAYSIZE(szTemp));
296 | pszValue = szTemp;
297 | }
298 | else
299 | {
300 | pszValue = "";
301 | }
302 | }
303 |
304 | SetDlgItemTextExA(hwndDlg, IDC_WINDOW_DPI_AWARENESS, pszValue);
305 |
306 | // Band field
307 |
308 | if (fValid)
309 | {
310 | if (s_pfnGetWindowBand)
311 | {
312 | DWORD dwBand = 0;
313 | s_pfnGetWindowBand(hwnd, &dwBand);
314 |
315 | sprintf_s(szTemp, ARRAYSIZE(szTemp), "%s (%u)",
316 | dwBand < ARRAYSIZE(s_pszWindowBands) ? s_pszWindowBands[dwBand] : "Unknown", dwBand);
317 | pszValue = szTemp;
318 | }
319 | else
320 | {
321 | pszValue = "";
322 | }
323 | }
324 |
325 | SetDlgItemTextExA(hwndDlg, IDC_WINDOW_BAND, pszValue);
326 |
327 | }
328 |
329 | void MarkProcessAsPerMonitorDpiAware()
330 | {
331 | InitializeDpiApis();
332 |
333 | if (s_pfnSetProcessDpiAwarenessContext)
334 | {
335 | s_pfnSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
336 | }
337 | }
338 |
339 | void MarkProcessAsSystemDpiAware()
340 | {
341 | InitializeDpiApis();
342 |
343 | if (s_pfnSetProcessDpiAwarenessContext)
344 | {
345 | s_pfnSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
346 | }
347 | }
348 |
349 | UINT g_SystemDPI = 0;
350 |
351 | int DPIScale(HWND hwnd, int value)
352 | {
353 | // If the system supports it (Windows 10+) then we use GetDpiForWindow
354 | // to determine the DPI associated with the window. Otherwise, we will
355 | // query the 'system' DPI that the winspy process is running under via
356 | // GetDeviceCaps+LOGPIXELSX on a screen DC.
357 |
358 | if (g_SystemDPI == 0)
359 | {
360 | InitializeDpiApis();
361 |
362 | HDC hdc = GetDC(NULL);
363 | g_SystemDPI = GetDeviceCaps(hdc, LOGPIXELSX);
364 | DeleteDC(hdc);
365 | }
366 |
367 | int dpi = 0;
368 |
369 | if (s_pfnGetDpiForWindow)
370 | {
371 | dpi = s_pfnGetDpiForWindow(hwnd);
372 | }
373 | else
374 | {
375 | dpi = g_SystemDPI;
376 | }
377 |
378 | return MulDiv(value, dpi, 96);
379 | }
380 |
--------------------------------------------------------------------------------
/src/DisplayProcessInfo.c:
--------------------------------------------------------------------------------
1 | //
2 | // DisplayProcessInfo.c
3 | // Copyright (c) 2002 by J Brown
4 | // Freeware
5 | //
6 | // Fill the process-tab-pane with process info for the
7 | // specified window.
8 | //
9 |
10 | #include "WinSpy.h"
11 |
12 | #include "Utils.h"
13 | #include
14 | #include
15 | #include "resource.h"
16 | #include
17 |
18 | void DescribeProcessDpiAwareness(DWORD dwProcessId, PSTR pszAwareness, size_t cchAwareness, PSTR pszDpi, size_t cchDpi);
19 | BOOL IsGetSystemDpiForProcessPresent();
20 |
21 | typedef BOOL(WINAPI * EnumProcessModulesProc)(HANDLE, HMODULE *, DWORD, PDWORD);
22 | typedef DWORD(WINAPI * GetModuleBaseNameProc)(HANDLE, HMODULE, PWSTR, DWORD);
23 | typedef DWORD(WINAPI * GetModuleFileNameExProc)(HANDLE, HMODULE, PWSTR, DWORD);
24 |
25 | typedef BOOL(WINAPI * QueryFullProcessImageNameProc)(HANDLE hProcess, DWORD dwFlags, PWSTR lpExeName, PDWORD lpdwSize);
26 |
27 | typedef HRESULT(WINAPI * GetThreadDescriptionProc)(HANDLE hThread, PWSTR *ppszThreadDescription);
28 |
29 | HRESULT TryGetThreadDescription(HANDLE hThread, PWSTR *ppszThreadDescription) {
30 | static GetThreadDescriptionProc fnGetThreadDescription = NULL;
31 |
32 | if (!fnGetThreadDescription)
33 | {
34 | fnGetThreadDescription = (GetThreadDescriptionProc)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetThreadDescription");
35 |
36 | if (!fnGetThreadDescription)
37 | return E_FAIL;
38 | }
39 |
40 | return fnGetThreadDescription(hThread, ppszThreadDescription);
41 | }
42 |
43 | BOOL GetProcessNameByPid1(DWORD dwProcessId, WCHAR szName[], DWORD nNameSize, WCHAR szPath[], DWORD nPathSize)
44 | {
45 | HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
46 | PROCESSENTRY32 pe = { sizeof(pe) };
47 | BOOL fFound = FALSE;
48 |
49 | szPath[0] = '\0';
50 | szName[0] = '\0';
51 |
52 | if (Process32First(h, &pe))
53 | {
54 | do
55 | {
56 | if (pe.th32ProcessID == dwProcessId)
57 | {
58 | if (szName)
59 | {
60 | lstrcpyn(szName, pe.szExeFile, nNameSize);
61 | }
62 |
63 | if (szPath)
64 | {
65 | //OpenProcess(
66 | lstrcpyn(szPath, pe.szExeFile, nPathSize);
67 | }
68 |
69 | fFound = TRUE;
70 | break;
71 | }
72 | } while (Process32Next(h, &pe));
73 | }
74 |
75 | CloseHandle(h);
76 |
77 | return fFound;
78 | }
79 |
80 |
81 | //
82 | // This uses PSAPI.DLL, which is only available under NT/2000/XP I think,
83 | // so we dynamically load this library, so that we can still run under 9x.
84 | //
85 | // dwProcessId [in]
86 | // szName [out]
87 | // nNameSize [in]
88 | // szPath [out]
89 | // nPathSize [in]
90 | //
91 | BOOL GetProcessNameByPid_BelowVista(DWORD dwProcessId, WCHAR szName[], DWORD nNameSize, WCHAR szPath[], DWORD nPathSize)
92 | {
93 | HMODULE hPSAPI;
94 | HANDLE hProcess;
95 |
96 | HMODULE hModule;
97 | DWORD dwNumModules;
98 |
99 | EnumProcessModulesProc fnEnumProcessModules;
100 | GetModuleBaseNameProc fnGetModuleBaseName;
101 | GetModuleFileNameExProc fnGetModuleFileNameEx;
102 |
103 | // Attempt to load Process Helper library
104 | hPSAPI = LoadLibrary(L"psapi.dll");
105 |
106 | if (!hPSAPI)
107 | {
108 | szName[0] = '\0';
109 | return FALSE;
110 | }
111 |
112 | // OK, we have access to the PSAPI functions, so open the process
113 | hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId);
114 | if (!hProcess)
115 | {
116 | FreeLibrary(hPSAPI);
117 | return FALSE;
118 | }
119 |
120 |
121 | fnEnumProcessModules = (EnumProcessModulesProc)GetProcAddress(hPSAPI, "EnumProcessModules");
122 | fnGetModuleBaseName = (GetModuleBaseNameProc)GetProcAddress(hPSAPI, "GetModuleBaseNameW");
123 | fnGetModuleFileNameEx = (GetModuleFileNameExProc)GetProcAddress(hPSAPI, "GetModuleFileNameExW");
124 |
125 | if (!fnEnumProcessModules || !fnGetModuleBaseName)
126 | {
127 | CloseHandle(hProcess);
128 | FreeLibrary(hPSAPI);
129 | return FALSE;
130 | }
131 |
132 | // Find the first module
133 | if (fnEnumProcessModules(hProcess, &hModule, sizeof(hModule), &dwNumModules))
134 | {
135 | // Now get the module name
136 | if (szName)
137 | fnGetModuleBaseName(hProcess, hModule, szName, nNameSize);
138 |
139 | // get module filename
140 | if (szPath)
141 | fnGetModuleFileNameEx(hProcess, hModule, szPath, nPathSize);
142 | }
143 | else
144 | {
145 | CloseHandle(hProcess);
146 | FreeLibrary(hPSAPI);
147 | return FALSE;
148 | }
149 |
150 | CloseHandle(hProcess);
151 | FreeLibrary(hPSAPI);
152 |
153 | return TRUE;
154 | }
155 |
156 | BOOL GetProcessNameByPid(DWORD dwProcessId, WCHAR szName[], DWORD nNameSize, WCHAR szPath[], DWORD nPathSize)
157 | {
158 | static QueryFullProcessImageNameProc fnQueryFullProcessImageName = NULL;
159 | HANDLE hProcess;
160 | DWORD dwSize;
161 | WCHAR *pName;
162 | BOOL bSucceeded;
163 |
164 | if (!fnQueryFullProcessImageName)
165 | {
166 | #ifdef UNICODE
167 | fnQueryFullProcessImageName = (QueryFullProcessImageNameProc)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "QueryFullProcessImageNameW");
168 | #else
169 | fnQueryFullProcessImageName = (QueryFullProcessImageNameProc)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "QueryFullProcessImageNameA");
170 | #endif
171 |
172 | if (!fnQueryFullProcessImageName)
173 | return GetProcessNameByPid_BelowVista(dwProcessId, szName, nNameSize, szPath, nPathSize);
174 | }
175 |
176 | bSucceeded = FALSE;
177 |
178 | hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId);
179 | if (hProcess)
180 | {
181 | dwSize = nPathSize;
182 |
183 | if (fnQueryFullProcessImageName(hProcess, 0, szPath, &dwSize))
184 | {
185 | pName = wcsrchr(szPath, '\\');
186 | if (pName)
187 | {
188 | wcsncpy_s(szName, nNameSize, pName + 1, _TRUNCATE);
189 | bSucceeded = TRUE;
190 | }
191 | }
192 |
193 | CloseHandle(hProcess);
194 | }
195 |
196 | return bSucceeded;
197 | }
198 |
199 |
200 | //
201 | // Update the Process tab for the specified window
202 | //
203 | void UpdateProcessTab(HWND hwnd, DWORD dwOverridePID)
204 | {
205 | DWORD dwProcessId = 0;
206 | DWORD dwThreadId = 0;
207 | WCHAR ach[32];
208 | WCHAR szPath[MAX_PATH];
209 | BOOL fValid;
210 | HWND hwndDlg = WinSpyTab[PROCESS_TAB].hwnd;
211 | PCWSTR pszDefault = L"";
212 |
213 | if (hwnd)
214 | {
215 | if (IsWindow(hwnd))
216 | {
217 | fValid = TRUE;
218 | dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId);
219 | }
220 | else
221 | {
222 | fValid = FALSE;
223 | pszDefault = szInvalidWindow;
224 | }
225 | }
226 | else
227 | {
228 | fValid = FALSE;
229 | dwProcessId = dwOverridePID;
230 | }
231 |
232 | // Process Id
233 |
234 | if (dwProcessId)
235 | {
236 | FormatDlgItemText(hwndDlg, IDC_PID, L"%08X (%u)", dwProcessId, dwProcessId);
237 | }
238 | else
239 | {
240 | SetDlgItemTextEx(hwndDlg, IDC_PID, pszDefault);
241 | }
242 |
243 |
244 | // Thread Id
245 |
246 | if (fValid)
247 | {
248 | PWSTR pszThreadDescription = NULL;
249 | HANDLE hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, dwThreadId);
250 | if (hThread) {
251 | if (FAILED(TryGetThreadDescription(hThread, &pszThreadDescription))) {
252 | pszThreadDescription = NULL;
253 | }
254 |
255 | CloseHandle(hThread);
256 | }
257 |
258 | FormatDlgItemText(hwndDlg, IDC_TID, L"%08X (%u)%s%s", dwThreadId, dwThreadId,
259 | (pszThreadDescription && *pszThreadDescription) ? L" - " : L"", pszThreadDescription ? pszThreadDescription : L"");
260 |
261 | if (pszThreadDescription) {
262 | LocalFree(pszThreadDescription);
263 | }
264 | }
265 | else
266 | {
267 | SetDlgItemTextEx(hwndDlg, IDC_TID, pszDefault);
268 | }
269 |
270 |
271 | // Try to get process name and path
272 | if (dwProcessId && GetProcessNameByPid(dwProcessId, ach, ARRAYSIZE(ach),
273 | szPath, ARRAYSIZE(szPath)))
274 | {
275 | SetDlgItemTextEx(hwndDlg, IDC_PROCESSNAME, ach);
276 | SetDlgItemTextEx(hwndDlg, IDC_PROCESSPATH, szPath);
277 | }
278 | else
279 | {
280 | SetDlgItemTextEx(hwndDlg, IDC_PROCESSNAME, fValid ? L"N/A" : pszDefault);
281 | SetDlgItemTextEx(hwndDlg, IDC_PROCESSPATH, fValid ? L"N/A" : pszDefault);
282 | }
283 |
284 | if (dwProcessId)
285 | {
286 | CHAR szMode[100];
287 | CHAR szDpi[100];
288 |
289 | DescribeProcessDpiAwareness(dwProcessId, szMode, ARRAYSIZE(szMode), szDpi, ARRAYSIZE(szDpi));
290 |
291 | SetDlgItemTextExA(hwndDlg, IDC_PROCESS_DPI_AWARENESS, szMode);
292 | SetDlgItemTextExA(hwndDlg, IDC_PROCESS_SYSTEM_DPI, szDpi);
293 |
294 | if (!IsGetSystemDpiForProcessPresent())
295 | {
296 | ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESS_SYSTEM_DPI_LABEL), SW_HIDE);
297 | ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESS_SYSTEM_DPI), SW_HIDE);
298 | }
299 | }
300 | else
301 | {
302 | SetDlgItemTextEx(hwndDlg, IDC_PROCESS_DPI_AWARENESS, pszDefault);
303 | SetDlgItemTextEx(hwndDlg, IDC_PROCESS_SYSTEM_DPI, pszDefault);
304 | }
305 | }
306 |
307 |
308 | WCHAR szWarning1[] = L"Are you sure you want to close this process?";
309 | WCHAR szWarning2[] = L"WARNING: Terminating a process can cause undesired\r\n"\
310 | L"results including loss of data and system instability. The\r\n"\
311 | L"process will not be given the chance to save its state or\r\n"\
312 | L"data before it is terminated. Are you sure you want to\r\n"\
313 | L"terminate the process?";
314 |
315 | void ShowProcessContextMenu(HWND hwndParent, INT x, INT y, BOOL fForButton, HWND hwnd, DWORD dwProcessId)
316 | {
317 | DWORD dwThreadId = 0;
318 | HMENU hMenu, hPopup;
319 | UINT uCmd;
320 | DWORD dwFlags;
321 |
322 | hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENU_PROCESS));
323 | hPopup = GetSubMenu(hMenu, 0);
324 |
325 | if (hwnd)
326 | {
327 | dwProcessId = 0;
328 | (void)GetWindowThreadProcessId(hwnd, &dwProcessId);
329 | }
330 | else
331 | {
332 | // If there is no current window, then we are working off a process
333 | // node selected in the window tree. The 'End Process (safe)' option
334 | // is currently built around posting WM_QUIT to the current window's
335 | // thread. For now, we just disable the option because we don't
336 | // know what thread to target. This could be enabled by enumerating
337 | // the windows in the process to pick a suitable thread.
338 |
339 | EnableMenuItem(hPopup, IDM_WINSPY_POSTQUIT, MF_BYCOMMAND | MF_GRAYED);
340 | }
341 |
342 | if (fForButton)
343 | {
344 | dwFlags = TPM_RIGHTALIGN | TPM_TOPALIGN | TPM_RETURNCMD;
345 | }
346 | else
347 | {
348 | dwFlags = TPM_RIGHTBUTTON | TPM_RETURNCMD;
349 | }
350 |
351 | uCmd = TrackPopupMenu(hPopup, dwFlags, x, y, 0, hwndParent, 0);
352 |
353 | switch (uCmd)
354 | {
355 | case IDM_WINSPY_FINDEXE:
356 | {
357 | WCHAR szExplorer[MAX_PATH];
358 | WCHAR szName[32];
359 | WCHAR szPath[MAX_PATH];
360 |
361 | if (GetProcessNameByPid(dwProcessId, szName, ARRAYSIZE(szName), szPath, ARRAYSIZE(szPath)))
362 | {
363 | swprintf_s(szExplorer, ARRAYSIZE(szExplorer), L"/select,\"%s\"", szPath);
364 | ShellExecute(0, L"open", L"explorer", szExplorer, 0, SW_SHOW);
365 | }
366 | else
367 | {
368 | MessageBox(hwndParent, L"Invalid Process Id", szAppName, MB_OK | MB_ICONWARNING);
369 | }
370 | }
371 | break;
372 |
373 | // Forcibly terminate!
374 | case IDM_WINSPY_TERMINATE:
375 | {
376 | if (MessageBox(hwndParent, szWarning2, szAppName, MB_YESNO | MB_ICONWARNING) == IDYES)
377 | {
378 | HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
379 |
380 | if (hProcess)
381 | {
382 | TerminateProcess(hProcess, (UINT)-1);
383 | CloseHandle(hProcess);
384 | }
385 | else
386 | {
387 | MessageBox(hwndParent, L"Invalid Process Id", szAppName, MB_OK | MB_ICONWARNING);
388 | }
389 | }
390 |
391 | break;
392 | }
393 |
394 | // Cleanly exit. Won't work if app. is hung
395 | case IDM_WINSPY_POSTQUIT:
396 | {
397 | if (MessageBox(hwndParent, szWarning1, szAppName, MB_YESNO | MB_ICONWARNING) == IDYES)
398 | {
399 | PostThreadMessage(dwThreadId, WM_QUIT, 0, 0);
400 | }
401 | break;
402 | }
403 | }
404 |
405 | DestroyMenu(hMenu);
406 | }
407 |
--------------------------------------------------------------------------------
/src/WinSpy.h:
--------------------------------------------------------------------------------
1 | #ifndef WINSPY_INCLUDED
2 | #define WINSPY_INCLUDED
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #define STRICT
9 | #define WIN32_LEAN_AND_MEAN
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #ifndef DWM_CLOAKED_APP
19 | #define DWMWA_CLOAKED 14
20 | #define DWM_CLOAKED_APP 0x00000001
21 | #define DWM_CLOAKED_SHELL 0x00000002
22 | #define DWM_CLOAKED_INHERITED 0x00000004
23 | #endif
24 |
25 | #define QUOTE(arg) #arg
26 | #define STRINGIZE(arg) QUOTE(arg)
27 |
28 | #define UNREFERENCED_PARAMETER(P) (P)
29 | #define IDM_WINSPY_ABOUT 100
30 |
31 | //
32 | // Define a structure for each property page in
33 | // the main window
34 | //
35 | typedef struct
36 | {
37 | HWND hwnd;
38 | PCWSTR szText;
39 | UINT id;
40 | DLGPROC dlgproc;
41 | } DialogTab;
42 |
43 | extern DialogTab WinSpyTab[];
44 |
45 | #define GENERAL_TAB 0
46 | #define STYLE_TAB 1
47 | #define PROPERTY_TAB 2
48 | #define CLASS_TAB 3
49 | #define WINDOW_TAB 4
50 | #define PROCESS_TAB 5
51 | #define DPI_TAB 6
52 | #define NUMTABCONTROLITEMS 7
53 |
54 | #define MAX_STYLE_NAME_CCH 60
55 |
56 | //
57 | // Extended Style table. 1 per window class
58 | //
59 |
60 | //
61 | // There are 2 sorts of "style constants":
62 | // 1. Single style. This is a logical "property" of the window (e.g.: WS_OVERLAPPED).
63 | // The presence of that property in a window's styles value is determined by a set of bits and a mask containing those bits.
64 | // Essentially, by masking the styles value with the mask, we obtain an "enum" value, and this style represents one value for that enum.
65 | // In most cases (e.g.: WS_MINIMIZE), that mask contains just one bit whose value of "1" indicates presence of this style
66 | // and "0" - absence of this style. But sometimes, the "0" value has its own style constant (e.g.: SBS_HORZ).
67 | // And sometimes, the mask contains more than 1 bit (e.g.: WS_OVERLAPPED).
68 | // 2. Combination style. This is a constant that represents a set of several single styles with non-overlapping masks
69 | // (e.g.: WS_OVERLAPPEDWINDOW). For such style to be present in the styles value, each of its components must be present.
70 | // Therefore, its presence can be checked using the value that is the "|" of all contained styles' values
71 | // and the mask that is the "|" of all contained styles' masks.
72 | //
73 | // Thus, the presence of any style in the styles value can be defined by 2 DWORD numbers: the mask and the matching value.
74 | //
75 | // Sometimes, applicability of a style depends on presence of another style.
76 | // For instance, the same bit would mean WS_TABSTOP if WS_CHILD is present or WS_MAXIMIZEBOX if WS_SYSMENU is present
77 | // (and, as experiment showed, even mean both if both those dependencies are present, which is obviously unintended and confusing).
78 | // Hopefully we can find a dependency style (not necessarily one of the predefined constants)
79 | // with no nesting dependencies for every style that we want to be able to display.
80 | // This way, we can implement a smart "set the style" functionality: not only set the actual value of the style's bits,
81 | // but also set its dependency's bits so that we make the style applicable and present at once.
82 | // This way, a comprehensive definition of a style will contain:
83 | // - style's value
84 | // - style's mask
85 | // - [optional] dependency style's value
86 | // - [optional] dependency style's mask
87 | //
88 | // To simplify style definitions, the "extraMask" fields will contain the bits that need to be "|"'ed with the value
89 | // in order to get the actual mask. This allows this field to default to 0 for the vast majority of typical cases
90 | // where the mask is equal to the value.
91 |
92 | typedef struct
93 | {
94 | PCWSTR name; // Textual name of style
95 | DWORD value; // The value of the style
96 | DWORD extraMask; // The extra bits determining the mask for testing presence of the style
97 |
98 | // This style is only applicable if the following style is present:
99 | DWORD dependencyValue;
100 | DWORD dependencyExtraMask;
101 | } StyleLookupEx;
102 |
103 | // Because static_assert is a statement which is not an expression, it cannot be used where an expression is expected.
104 | // Therefore, we define an expression loosely equivalent to a static_assert here
105 | // which would cause a compilation error if the condition is false.
106 | #define value_with_static_assert(value, condition) 1 ? (value) : (sizeof(int[condition]), (value))
107 |
108 | #define CHECKEDNAMEANDVALUE_(name, value) L##name, value_with_static_assert((UINT)(value), ARRAYSIZE(name) < MAX_STYLE_NAME_CCH)
109 | #define NAMEANDVALUE_(value) CHECKEDNAMEANDVALUE_(#value, value)
110 |
111 | //
112 | // Define some masks that are not defined in the Windows headers
113 | //
114 | #define SBS_DIR_MASK SBS_HORZ | SBS_VERT //0x0001
115 |
116 | //
117 | // Use this structure to list each window class with its
118 | // associated style table and, optionally, a message to send to the window
119 | // to retrieve this set of control-specific extended styles
120 | //
121 | typedef struct
122 | {
123 | PCWSTR ClassName;
124 | StyleLookupEx *Styles;
125 | BOOL UsesComctlStyles;
126 | StyleLookupEx *StylesExtra;
127 | DWORD GetMessage;
128 | DWORD SetMessage;
129 | }
130 | ClassStyleInfo;
131 |
132 | #define STYLE_FLAVOR_REGULAR 1 // GWL_STYLE
133 | #define STYLE_FLAVOR_EX 2 // GWL_EXSTYLE
134 | #define STYLE_FLAVOR_EXTRA 3 // Class private styles, e.g. LVM_GETEXTENDEDLISTVIEWSTYLE
135 |
136 | ClassStyleInfo* FindClassStyleInfo(HWND hwnd);
137 | DWORD GetWindowExtraStyles(HWND hwnd, ClassStyleInfo* pClassInfo, DWORD* pdw);
138 | void FillStyleListForEditing(HWND hwndTarget, HWND hwndList, UINT flavor, DWORD dwStyles);
139 | void ShowWindowStyleEditor(HWND hwndParent, HWND hwndTarget, UINT flavor);
140 |
141 |
142 | //
143 | // Useful functions!
144 | //
145 | BOOL FunkyList_MeasureItem(HWND hwnd, MEASUREITEMSTRUCT *mis);
146 | BOOL FunkyList_DrawItem(HWND hwnd, UINT uCtrlId, DRAWITEMSTRUCT *dis);
147 |
148 | //
149 | // WinSpy layout functions
150 | //
151 | void ToggleWindowLayout(HWND hwnd);
152 | void SetWindowLayout(HWND hwnd, UINT uLayout);
153 | UINT GetWindowLayout(HWND hwnd);
154 | void ForceVisibleDisplay(HWND hwnd);
155 | void UpdateMainWindowText();
156 | void UpdateGlobalHotkey();
157 |
158 | #define WINSPY_LAYOUT_NO 0
159 | #define WINSPY_MINIMIZED 1
160 | #define WINSPY_NORMAL 2
161 | #define WINSPY_EXPANDED 3
162 | #define WINSPY_LASTMAX 4 // Only use with SetWindowLayout
163 | // (chooses between normal/expanded)
164 |
165 | //
166 | // WinSpy message handler functions
167 | //
168 |
169 | UINT WinSpyDlg_Size(HWND hwnd, WPARAM wParam, LPARAM lParam);
170 | UINT WinSpyDlg_Sizing(UINT nSide, RECT *prc);
171 | UINT WinSpyDlg_WindowPosChanged(HWND hwnd, WINDOWPOS *wp);
172 | UINT WinSpyDlg_EnterSizeMove(HWND hwnd);
173 | UINT WinSpyDlg_ExitSizeMove(HWND hwnd);
174 | UINT_PTR WinSpyDlg_NCHitTest(HWND hwnd, WPARAM wParam, LPARAM lParam);
175 |
176 | UINT WinSpyDlg_CommandHandler(HWND hwnd, WPARAM wParam, LPARAM lParam);
177 | UINT WinSpyDlg_SysMenuHandler(HWND hwnd, WPARAM wParam, LPARAM lParam);
178 | UINT WinSpyDlg_TimerHandler(UINT_PTR uTimerId);
179 |
180 | void WinSpyDlg_SizeContents(HWND hwnd);
181 |
182 | UINT WinSpy_PopupCommandHandler(HWND hwndDlg, UINT uCmdId, HWND hwndTarget);
183 | void WinSpy_SetupPopupMenu(HMENU hMenu, HWND hwndTarget);
184 |
185 |
186 | int WINAPI GetRectWidth(RECT *rect);
187 | int WINAPI GetRectHeight(RECT *rect);
188 |
189 | BOOL IsWindowMinimized(HWND hwnd);
190 |
191 | //
192 | // Dialog box procedures for each dialog tab.
193 | //
194 | INT_PTR CALLBACK GeneralDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
195 | INT_PTR CALLBACK StyleDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
196 | INT_PTR CALLBACK WindowDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
197 | INT_PTR CALLBACK PropertyDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
198 | INT_PTR CALLBACK ProcessDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
199 | INT_PTR CALLBACK ClassDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
200 | INT_PTR CALLBACK DpiDlgProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
201 |
202 | // Top-level
203 | void DisplayWindowInfo(HWND hwnd);
204 |
205 | void UpdateActiveTab();
206 | void UpdateWindowTab(HWND hwnd);
207 | void UpdateClassTab(HWND hwnd);
208 | void UpdateStyleTab(HWND hwnd);
209 | void UpdateGeneralTab(HWND hwnd);
210 | void UpdatePropertyTab(HWND hwnd);
211 | void UpdateProcessTab(HWND hwnd, DWORD dwOverridePID);
212 | void UpdateDpiTab(HWND hwnd);
213 |
214 | void UpdateScrollbarInfo(HWND hwnd);
215 | void UpdateWndProcControls(HWND hwnd, HWND hwndDlg, PVOID clsproc);
216 |
217 | void GetRemoteInfo();
218 |
219 | void ExitWinSpy(HWND hwnd, UINT uCode);
220 |
221 | //
222 | // Menu and System Menu functions
223 | //
224 | //
225 | void CheckSysMenu(HWND hwnd, UINT uItemId, BOOL fChecked);
226 | void SetSysMenuIconFromLayout(HWND hwnd, UINT layout);
227 |
228 | void ShowEditSizeDlg(HWND hwndParent, HWND hwndTarget);
229 | void ShowPosterDlg(HWND hwndParent, HWND hwndTarget);
230 | void ShowBroadcasterDlg(HWND hwndParent);
231 | void ShowWindowPropertyEditor(HWND hwndParent, HWND hwndTarget, BOOL bAddNew);
232 | void ShowOptionsDlg(HWND hwndParent);
233 | void ShowAboutDlg(HWND hwndParent);
234 |
235 | void LoadSettings(void);
236 | void SaveSettings(void);
237 |
238 | BOOL GetRemoteWindowInfo(HWND hwnd, WNDCLASSEX *pClass,
239 | WNDPROC *pProc, WCHAR *pszText, int nTextLen);
240 |
241 | BOOL RemoveTabCtrlFlicker(HWND hwndTab);
242 |
243 | void VerboseClassName(WCHAR ach[], size_t cch, WORD atom);
244 |
245 | void InitStockStyleLists();
246 | BOOL GetProcessNameByPid(DWORD dwProcessId, WCHAR szName[], DWORD nNameSize, WCHAR szPath[], DWORD nPathSize);
247 |
248 | void ShowProcessContextMenu(HWND hwndParent, INT x, INT y, BOOL fForButton, HWND hwnd, DWORD dwProcessId);
249 |
250 | //
251 | // Pinned-window support
252 | //
253 | void GetPinnedPosition(HWND, POINT *);
254 | BOOL WinSpy_ZoomTo(HWND hwnd, UINT uCorner);
255 | void SetPinState(BOOL fPinned);
256 |
257 | //
258 | // Pinned-window constants
259 | //
260 | #define PINNED_TOPLEFT 0 // notice the bit-positions:
261 | #define PINNED_TOPRIGHT 1 // (bit 0 -> 0 = left, 1 = right)
262 | #define PINNED_BOTTOMRIGHT 3 // (bit 1 -> 0 = top, 1 = bottom)
263 | #define PINNED_BOTTOMLEFT 2
264 |
265 | #define PINNED_NONE PINNED_TOPLEFT
266 | #define PINNED_LEFT 0
267 | #define PINNED_RIGHT 1
268 | #define PINNED_TOP 0
269 | #define PINNED_BOTTOM 2
270 |
271 | //
272 | // Hotkey IDs
273 | //
274 | #define HOTKEY_ID_SELECT_WINDOW_UNDER_CURSOR 1001
275 |
276 |
277 | //
278 | // Global variables!! These just control WinSpy behavior
279 | //
280 | typedef struct
281 | {
282 | BOOL fAlwaysOnTop;
283 | BOOL fClassThenText;
284 | BOOL fEnableToolTips;
285 | BOOL fFullDragging;
286 | BOOL fMinimizeWinSpy;
287 | BOOL fPinWindow;
288 | BOOL fShowDimmed;
289 | BOOL fShowHidden;
290 | BOOL fShowInCaption;
291 | BOOL fSaveWinPos;
292 | BOOL fShowDesktopRoot;
293 | UINT uTreeInclude;
294 | BOOL fShowHiddenInList;
295 | BOOL fEnableHotkey;
296 | WORD wHotkey; // Encoded as per HKM_GETHOTKEY
297 |
298 | // These two variables help us to position WinSpy++ intelligently when it resizes.
299 | POINT ptPinPos;
300 | UINT uPinnedCorner;
301 | } Options;
302 |
303 | extern Options g_opts;
304 |
305 | //
306 | // Application global variables
307 | //
308 | extern HINSTANCE g_hInst;
309 |
310 | #define szAppName L"WinSpy++"
311 |
312 | #define szInvalidWindow L"(invalid window)"
313 |
314 | extern HWND g_hwndPin; // Toolbar with pin bitmap
315 | extern HWND g_hwndSizer; // Sizing grip for bottom-right corner
316 | extern HWND g_hwndToolTip; // tooltip for main window controls only
317 |
318 | //
319 | // Spy-window globals
320 | //
321 | //
322 | extern HWND g_hCurWnd;
323 | extern WNDPROC g_WndProc;
324 | extern BOOL g_fPassword;
325 | extern BOOL g_fTriedRemote;
326 | extern WCHAR g_szPassword[];
327 | extern WCHAR g_szClassName[];
328 |
329 | extern DWORD g_dwSelectedPID;
330 | extern BOOL g_fShowClientRectAsMargins;
331 | extern BOOL g_fFirstInstance;
332 |
333 |
334 | //
335 | // Useful SetWindowPos constants (saves space!)
336 | //
337 | #define SWP_SIZEONLY (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE)
338 | #define SWP_MOVEONLY (SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE)
339 | #define SWP_ZONLY (SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)
340 | #define SWP_SHOWONLY (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_SHOWWINDOW)
341 | #define SWP_HIDEONLY (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_HIDEWINDOW)
342 |
343 | //
344 | // Window tree
345 | //
346 |
347 | #define WINLIST_INCLUDE_HANDLE 1 // handle, classname
348 | #define WINLIST_INCLUDE_CLASS 2 // classname, caption
349 | #define WINLIST_INCLUDE_ALL 3 // handle, caption, classname
350 |
351 | void WindowTree_Initialize(HWND hwndTree);
352 | void WindowTree_Destroy();
353 | void WindowTree_Refresh(HWND hwndToSelect, BOOL fSetFocus);
354 | void WindowTree_OnRightClick(NMHDR *pnm);
355 | void WindowTree_OnSelectionChanged(NMHDR *pnm);
356 | void WindowTree_Locate(HWND hwnd);
357 | HWND WindowTree_GetSelectedWindow();
358 | void WindowTree_RefreshWindowNode(HWND hwnd);
359 |
360 |
361 | //
362 | // DPI related helpers.
363 | //
364 |
365 | int DPIScale(HWND hwnd, int value);
366 | void MarkProcessAsPerMonitorDpiAware();
367 | void MarkProcessAsSystemDpiAware();
368 |
369 | #ifdef __cplusplus
370 | }
371 | #endif
372 |
373 | #endif
374 |
--------------------------------------------------------------------------------