├── 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 | --------------------------------------------------------------------------------