Repository: zodiacon/WinSpy Branch: master Commit: da6a441695fa Files: 63 Total size: 224.8 KB Directory structure: gitextract_xjr65t7e/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── WinSpy/ │ ├── AboutDlg.cpp │ ├── AboutDlg.h │ ├── AutomationTreeView.cpp │ ├── AutomationTreeView.h │ ├── DialogHelper.h │ ├── FindWindowDlg.cpp │ ├── FindWindowDlg.h │ ├── FormatHelper.cpp │ ├── FormatHelper.h │ ├── FrameWindowHelper.h │ ├── HookHelper.cpp │ ├── HookHelper.h │ ├── IconHelper.cpp │ ├── IconHelper.h │ ├── ImageIconCache.cpp │ ├── ImageIconCache.h │ ├── Interfaces.h │ ├── MainFrm.cpp │ ├── MainFrm.h │ ├── MessageDecoder.cpp │ ├── MessageDecoder.h │ ├── MessagesView.cpp │ ├── MessagesView.h │ ├── ProcessHelper.cpp │ ├── ProcessHelper.h │ ├── ProcessesView.cpp │ ├── ProcessesView.h │ ├── SecurityHelper.cpp │ ├── SecurityHelper.h │ ├── TreeViewManager.h │ ├── ViewBase.h │ ├── WinSpy.cpp │ ├── WinSpy.h │ ├── WinSpy.rc │ ├── WinSpy.vcxproj │ ├── WinSpy.vcxproj.filters │ ├── WindowGeneralPage.cpp │ ├── WindowGeneralPage.h │ ├── WindowHelper.cpp │ ├── WindowHelper.h │ ├── WindowWindowsPage.cpp │ ├── WindowWindowsPage.h │ ├── WindowsListView.cpp │ ├── WindowsListView.h │ ├── WindowsView.cpp │ ├── WindowsView.h │ ├── pch.cpp │ ├── pch.h │ └── resource.h ├── WinSpy.sln └── WinSpyHook/ ├── WinSpyHook.def ├── WinSpyHook.vcxproj ├── WinSpyHook.vcxproj.filters ├── dllmain.cpp ├── hooks.cpp ├── hooks.h ├── pch.cpp └── pch.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- Backup*.rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # BeatPulse healthcheck temp database healthchecksdb ================================================ FILE: .gitmodules ================================================ [submodule "WTLHelper"] path = WTLHelper url = https://github.com/zodiacon/WTLHelper.git ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Pavel Yosifovich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # WinSpy Enhanced version of the classic Spy++ tool (WIP) ================================================ FILE: WinSpy/AboutDlg.cpp ================================================ // aboutdlg.cpp : implementation of the CAboutDlg class // ///////////////////////////////////////////////////////////////////////////// #include "pch.h" #include "resource.h" #include "aboutdlg.h" LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { CenterWindow(GetParent()); return TRUE; } LRESULT CAboutDlg::OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { EndDialog(wID); return 0; } ================================================ FILE: WinSpy/AboutDlg.h ================================================ // aboutdlg.h : interface of the CAboutDlg class // ///////////////////////////////////////////////////////////////////////////// #pragma once class CAboutDlg : public CDialogImpl { public: enum { IDD = IDD_ABOUTBOX }; BEGIN_MSG_MAP(CAboutDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(IDOK, OnCloseCmd) COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) END_MSG_MAP() // Handler prototypes (uncomment arguments if needed): // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); }; ================================================ FILE: WinSpy/AutomationTreeView.cpp ================================================ #include "pch.h" #include "AutomationTreeView.h" #include "WindowHelper.h" #pragma comment(lib, "oleacc") LRESULT CAutomationTreeView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { m_hWndClient = m_Splitter.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); m_Tree.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS, 0, IDC_TREE); m_List.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL); m_List.SetExtendedListViewStyle(LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); m_List.InsertColumn(0, L"Property", 0, 180); m_List.InsertColumn(1, L"Value", 0, 350); m_Tree.SetExtendedStyle(TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER); m_Tree.SetImageList(WindowHelper::GetImageList(), TVSIL_NORMAL); m_Splitter.SetSplitterPanes(m_Tree, m_List); m_Splitter.SetSplitterPosPct(35); InitTree(); return 0; } LRESULT CAutomationTreeView::OnNodeExpanding(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; if (tv->action != TVE_EXPAND) return FALSE; auto hItem = tv->itemNew.hItem; if (m_Tree.GetChildItem(hItem)) return FALSE; auto elem = (IUIAutomationElement*)m_Tree.GetItemData(hItem); if (elem) { EnumChildElements(m_spUIWalker, elem, hItem); return FALSE; } return TRUE; } LRESULT CAutomationTreeView::OnNodeExpanded(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; if (tv->action != TVE_COLLAPSE) return FALSE; // // delete child nodes // auto hItem = tv->itemNew.hItem; HTREEITEM hChild; while (hChild = m_Tree.GetChildItem(hItem)) m_Tree.DeleteItem(hChild); TVITEM tvi{ sizeof(tvi) }; tvi.cChildren = 1; tvi.mask = TVIF_CHILDREN; tvi.hItem = hItem; m_Tree.SetItem(&tvi); return FALSE; } LRESULT CAutomationTreeView::OnNodeSelected(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; auto hItem = tv->itemNew.hItem; auto data = (IUIAutomationElement*)m_Tree.GetItemData(tv->itemNew.hItem); UpdateProperties(data); return 0; } LRESULT CAutomationTreeView::OnNodeDeleted(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; auto data = (IUIAutomationElement*)m_Tree.GetItemData(tv->itemOld.hItem); if (data) data->Release(); return 0; } void CAutomationTreeView::OnActivate(bool active) { if (active) { UpdateUI(); } } CString CAutomationTreeView::GetColumnText(HWND, int row, int col) const { auto& p = m_Properties[row]; switch (col) { case 0: return p.Name; case 1: return p.Value.c_str(); } return L""; } HTREEITEM CAutomationTreeView::AddElement(IUIAutomationElement* e, HTREEITEM hParent, HTREEITEM hAfter) { CComBSTR name, cls, id; e->get_CurrentName(&name); e->get_CurrentClassName(&cls); CString text; text.Format(L"%s [%s]", name.m_str, cls.m_str); auto hItem = m_Tree.InsertItem(text, hParent, hAfter); e->AddRef(); m_Tree.SetItemData(hItem, (DWORD_PTR)e); return hItem; } void CAutomationTreeView::EnumChildElements(IUIAutomationTreeWalker* pWalker, IUIAutomationElement* root, HTREEITEM hParent, HTREEITEM hAfter) { CComPtr spElem; pWalker->GetFirstChildElement(root, &spElem); int pid = 0; while (spElem) { spElem->get_CurrentProcessId(&pid); if (pid != ::GetCurrentProcessId()) { auto node = AddElement(spElem, hParent, hAfter); CComPtr spChild; pWalker->GetFirstChildElement(spElem, &spChild); if (spChild) { TVITEM tvi{ sizeof(tvi) }; tvi.cChildren = 1; tvi.mask = TVIF_CHILDREN; tvi.hItem = node; m_Tree.SetItem(&tvi); } } CComPtr spNext; pWalker->GetNextSiblingElement(spElem, &spNext); spElem = spNext; } } void CAutomationTreeView::InitTree() { CWaitCursor wait; if (m_spUI == nullptr) { m_spUI.CoCreateInstance(__uuidof(CUIAutomation)); if (m_spUI == nullptr) return; } m_Tree.SetRedraw(FALSE); m_Tree.DeleteAllItems(); CComPtr spWalker; m_spUI->get_RawViewWalker(&spWalker); ATLASSERT(spWalker); m_spUIWalker = spWalker; CComPtr spRoot; m_spUI->GetRootElement(&spRoot); auto node = AddElement(spRoot); EnumChildElements(spWalker, spRoot, node); m_Tree.Expand(node, TVE_EXPAND); m_Tree.SetRedraw(TRUE); } void CAutomationTreeView::UpdateUI() { } void CAutomationTreeView::UpdateProperties(IUIAutomationElement* elem) { m_Properties.clear(); if (elem) { static const struct { PROPERTYID id; PCWSTR text; } props[] = { { UIA_NamePropertyId, L"Name" }, { UIA_RuntimeIdPropertyId, L"Runtime ID" }, { UIA_ClassNamePropertyId, L"Class Name" }, { UIA_ControlTypePropertyId, L"Control Type" }, { UIA_LocalizedControlTypePropertyId, L"Localized Control Type" }, { UIA_FullDescriptionPropertyId, L"Full Description" }, { UIA_AcceleratorKeyPropertyId, L"Accelerator Key" }, { UIA_AccessKeyPropertyId, L"Access Key" }, { UIA_AutomationIdPropertyId, L"Automation ID" }, { UIA_BoundingRectanglePropertyId, L"Bounding Rectangle" }, { UIA_CenterPointPropertyId, L"Center Point" }, { UIA_HasKeyboardFocusPropertyId, L"Has Focus" }, { UIA_HelpTextPropertyId, L"Help Text" }, { UIA_ItemStatusPropertyId, L"Item Status" }, { UIA_NativeWindowHandlePropertyId, L"Window Handle" }, { UIA_ProcessIdPropertyId, L"Process ID" }, { UIA_SizePropertyId, L"Size" }, { UIA_OrientationPropertyId, L"Orientation" }, { UIA_OutlineColorPropertyId, L"Outline Color" }, { UIA_FillColorPropertyId, L"Fill Color" }, { UIA_PositionInSetPropertyId, L"Position in Set" }, { UIA_OutlineThicknessPropertyId, L"Outline Thickness" }, { UIA_IsEnabledPropertyId, L"Enabled" }, { UIA_IsOffscreenPropertyId, L"Off Screen" }, { UIA_IsPasswordPropertyId, L"Password" }, { UIA_IsEnabledPropertyId, L"Editable" }, { UIA_ItemTypePropertyId, L"Item Type" }, { UIA_SizeOfSetPropertyId, L"Size of Set" }, { UIA_VisualEffectsPropertyId, L"Visual Effects" }, { UIA_LiveSettingPropertyId, L"Live Setting" }, { UIA_LevelPropertyId, L"Level" }, { UIA_IsPeripheralPropertyId, L"Peripheral" }, { UIA_IsKeyboardFocusablePropertyId, L"Keyboard Focusable" }, { UIA_IsDialogPropertyId, L"Dialog" }, { UIA_IsControlElementPropertyId, L"Control Element" }, { UIA_FrameworkIdPropertyId, L"Framework ID" }, }; for (auto& p : props) { CComVariant value; if (S_OK == elem->GetCurrentPropertyValue(p.id, &value)) { ItemData data; data.Value = FormatValue(m_spUI, value); if (data.Value.empty() && S_OK == value.ChangeType(VT_BSTR)) { data.Value = value.bstrVal; } if (!data.Value.empty()) { data.Name = p.text; m_Properties.push_back(std::move(data)); } } } } m_List.SetItemCount((int)m_Properties.size()); } std::wstring CAutomationTreeView::FormatValue(IUIAutomation* pUI, VARIANT const& value) { switch (value.vt) { case VT_I4: return std::format(L"{} (0x{:X})", value.intVal, value.intVal); case VT_UI4: return std::format(L"{} (0x{:X})", value.uintVal, value.uintVal); case VT_BOOL: return value.boolVal ? L"True" : L"False"; case VT_I4 | VT_ARRAY: { int* data, count; if (S_OK == pUI->IntSafeArrayToNativeArray(value.parray, &data, &count)) { std::wstring result(L"("); for (int i = 0; i < count; i++) { result += std::format(L"{}", data[i]); if (i < count - 1) result += L", "; } result += L")"; return result; } break; } case VT_R8 | VT_ARRAY: { RECT* rc; int count; if (S_OK == pUI->SafeArrayToRectNativeArray(value.parray, &rc, &count) && count > 0) { std::wstring result(L"("); for (int i = 0; i < count; i++) { result += std::format(L"[{},{}-{},{}]", rc[i].left, rc[i].top, rc[i].right, rc[i].bottom); if (i < count - 1) result += L", "; } result += L")"; return result; } else { double* data; count = value.parray->rgsabound[0].cElements; if (S_OK == ::SafeArrayAccessData(value.parray, (void**)&data)) { std::wstring result(L"("); for (int i = 0; i < count; i++) { result += std::format(L"{}", data[i]); if (i < count - 1) result += L", "; } ::SafeArrayUnaccessData(value.parray); result += L")"; return result; } } break; } } return L""; } ================================================ FILE: WinSpy/AutomationTreeView.h ================================================ #pragma once #include "ViewBase.h" #include #include #include #include struct IUIAutomationElement; struct IUIAutomationTreeWalker; class CAutomationTreeView : public CViewBase, public CVirtualListView, public CTreeViewHelper { public: explicit CAutomationTreeView(IMainFrame* frame) : CViewBase(frame) {} void OnActivate(bool active); CString GetColumnText(HWND, int row, int col) const; protected: enum { IDC_TREE = 123 }; BEGIN_MSG_MAP(CAutomationTreeView) MESSAGE_HANDLER(WM_CREATE, OnCreate) NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING, OnNodeExpanding) NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDED, OnNodeExpanded) NOTIFY_CODE_HANDLER(TVN_SELCHANGED, OnNodeSelected) NOTIFY_CODE_HANDLER(TVN_DELETEITEM, OnNodeDeleted) CHAIN_MSG_MAP(CTreeViewHelper) CHAIN_MSG_MAP(CVirtualListView) CHAIN_MSG_MAP(CViewBase) END_MSG_MAP() LRESULT OnTreeNodeRightClick(HTREEITEM hItem, CPoint const& pt); LRESULT OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt); private: HTREEITEM AddElement(IUIAutomationElement* element, HTREEITEM hParent = TVI_ROOT, HTREEITEM hAfter = TVI_SORT); void EnumChildElements(IUIAutomationTreeWalker* pWalker, IUIAutomationElement* root, HTREEITEM hParent = TVI_ROOT, HTREEITEM hAfter = TVI_SORT); void InitTree(); void UpdateUI(); void UpdateProperties(IUIAutomationElement* elem); static std::wstring FormatValue(IUIAutomation* pUI, VARIANT const& value); LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnNodeExpanding(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnNodeExpanded(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnNodeSelected(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnNodeDeleted(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); struct ItemData { PCWSTR Name; std::wstring Value; }; CCustomSplitterWindow m_Splitter; CTreeViewCtrl m_Tree; CListViewCtrl m_List; CComPtr m_spUIWalker; CComPtr m_spUI; std::vector m_Properties; }; ================================================ FILE: WinSpy/DialogHelper.h ================================================ #pragma once template class CDialogHelper { public: #ifdef IDI_OK void AdjustOKCancelButtons() { auto dlg = static_cast(this); CButton ok(dlg->GetDlgItem(IDOK)); if (ok) { CString text; ok.GetWindowText(text); ok.SetWindowText(L" " + text); ok.SetIcon(AtlLoadIconImage(IDI_OK, 0, 16, 16)); } CButton cancel(dlg->GetDlgItem(IDCANCEL)); if (cancel) { cancel.SetWindowText(L" Cancel"); cancel.SetIcon(AtlLoadIconImage(IDI_CANCEL, 0, 16, 16)); } } #endif bool AddIconToButton(WORD id, WORD icon, int size = 16) { auto dlg = static_cast(this); CButton button(dlg->GetDlgItem(id)); if (button) { button.SetIcon(AtlLoadIconImage(icon, 0, size, size)); CString text; button.GetWindowText(text); button.SetWindowText(L" " + text); } return (bool)button; } void SetDialogIcon(UINT icon) { auto dlg = static_cast(this); dlg->SetIcon(AtlLoadIconImage(icon, 0, 16, 16), FALSE); dlg->SetIcon(AtlLoadIconImage(icon, 0, 32, 32), TRUE); } void SetDialogIcon(HICON icon) { auto dlg = static_cast(this); dlg->SetIcon(icon, FALSE); dlg->SetIcon(icon, TRUE); } }; ================================================ FILE: WinSpy/FindWindowDlg.cpp ================================================ #include "pch.h" #include "resource.h" #include "FindWindowDlg.h" #include "WindowHelper.h" #include "ProcessHelper.h" #include "FormatHelper.h" HWND CFindWindowDlg::GetSelectedHwnd() const { return m_SelectedHwnd; } void CFindWindowDlg::ClearWindowDetails() { SetDlgItemText(IDC_HANDLE, L""); SetDlgItemText(IDC_TEXT, L""); SetDlgItemText(IDC_CLASSNAME, L""); SetDlgItemText(IDC_THREAD, L""); SetDlgItemText(IDC_PROCESS, L""); if (m_hCursorWnd) WindowHelper::HighlightBorder(m_hCursorWnd, false); m_WinDrag.ShowWindow(SW_SHOW); if (IsDlgButtonChecked(IDC_HIDE) == BST_CHECKED) { ::ShowWindow(m_pFrame->GetHwnd(), SW_SHOW); } } void CFindWindowDlg::SelectWindow(HWND hWnd) { CString text; text.Format(L"0x%zX", DWORD_PTR(hWnd)); SetDlgItemText(IDC_HANDLE, text); WCHAR clsName[128]; m_SelectedHwnd = hWnd; if (::GetClassName(hWnd, clsName, _countof(clsName))) SetDlgItemText(IDC_CLASSNAME, clsName); CWindow win(hWnd); win.GetWindowText(text); SetDlgItemText(IDC_TEXT, text); DWORD pid; auto tid = ::GetWindowThreadProcessId(hWnd, &pid); SetDlgItemInt(IDC_THREAD, tid, FALSE); text.Format(L"%s (%d)", (PCWSTR)ProcessHelper::GetProcessImageName(pid), pid); SetDlgItemText(IDC_PROCESS, text); } LRESULT CFindWindowDlg::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) { InitDynamicLayout(); SetDialogIcon(IDI_WINDOWSEARCH); m_WinDrag.SubclassWindow(GetDlgItem(IDC_TARGET)); m_WinDrag.SetIcon(AtlLoadIconImage(IDI_TARGET, 0, 32, 32)); m_DragCursor = AtlLoadIconImage(IDI_TARGET, 0, ::GetSystemMetrics(SM_CXCURSOR), ::GetSystemMetrics(SM_CYCURSOR)); return 0; } LRESULT CFindWindowDlg::OnCloseCmd(WORD, WORD id, HWND, BOOL&) { if (id == IDCANCEL && m_Capture) { ReleaseCapture(); m_Capture = false; ClearWindowDetails(); return 0; } if (id == IDOK) { CString text; GetDlgItemText(IDC_HANDLE, text); m_SelectedHwnd = (HWND)FormatHelper::ParseHex(text); } EndDialog(id); return 0; } LRESULT CFindWindowDlg::OnSearch(WORD, WORD id, HWND, BOOL&) { CString handle; GetDlgItemText(IDC_HANDLE, handle); if (!handle.IsEmpty()) { auto hWnd = (HWND)FormatHelper::ParseHex(handle); if (::IsWindow(hWnd)) { SelectWindow(hWnd); } else { AtlMessageBox(m_hWnd, (PCWSTR)(L"Cannot locate window with handle " + handle), L"Window Finder", MB_ICONERROR); } return 0; } CString text, cls; GetDlgItemText(IDC_TEXT, text); GetDlgItemText(IDC_CLASSNAME, cls); if (text.IsEmpty() && cls.IsEmpty()) { AtlMessageBox(m_hWnd, L"Specify handle, class and/or text to search", L"Window Finder", MB_ICONWARNING); return 0; } auto hWnd = ::FindWindowEx(nullptr, nullptr, cls, text); if (hWnd) SelectWindow(hWnd); else AtlMessageBox(m_hWnd, L"Cannot locate window with this class/text", L"Window Finder", MB_ICONERROR); return 0; } LRESULT CFindWindowDlg::OnMouseDown(UINT, WPARAM, LPARAM, BOOL&) { m_WinDrag.SetCapture(); m_Capture = true; m_WinDrag.ShowWindow(SW_HIDE); if (IsDlgButtonChecked(IDC_HIDE) == BST_CHECKED) { ::ShowWindow(m_pFrame->GetHwnd(), SW_HIDE); } return 0; } LRESULT CFindWindowDlg::OnMouseUp(UINT, WPARAM, LPARAM, BOOL&) { ReleaseCapture(); m_Capture = false; m_WinDrag.ShowWindow(SW_SHOW); SetCursor(AtlLoadSysCursor(IDC_ARROW)); if(m_hCursorWnd) WindowHelper::HighlightBorder(m_hCursorWnd, false); if (IsDlgButtonChecked(IDC_HIDE) == BST_CHECKED) { ::ShowWindow(m_pFrame->GetHwnd(), SW_SHOW); } return 0; } LRESULT CFindWindowDlg::OnMouseMove(UINT, WPARAM, LPARAM lp, BOOL&) { if (m_Capture) { ::SetCursor(m_DragCursor); CPoint pt{ GET_X_LPARAM(lp), GET_Y_LPARAM(lp) }; m_WinDrag.ClientToScreen(&pt); auto hWnd = ::WindowFromPoint(pt); DWORD pid, tid; if ((tid = ::GetWindowThreadProcessId(hWnd, &pid)) && pid == ::GetCurrentProcessId()) return 0; if (m_hCursorWnd && m_hCursorWnd != hWnd) WindowHelper::HighlightBorder(m_hCursorWnd, false); if (hWnd != m_hCursorWnd) { WindowHelper::HighlightBorder(hWnd); m_hCursorWnd.Detach(); m_hCursorWnd.Attach(hWnd); SelectWindow(hWnd); } } return 0; } ================================================ FILE: WinSpy/FindWindowDlg.h ================================================ #pragma once #include "Interfaces.h" #include "DialogHelper.h" class CFindWindowDlg : public CDialogImpl, public CDynamicDialogLayout, public CDialogHelper { public: enum { IDD = IDD_FINDWINDOW }; CFindWindowDlg(IMainFrame* frame) : m_pFrame(frame), m_WinDrag(this, 1) {} HWND GetSelectedHwnd() const; protected: BEGIN_MSG_MAP(CFindWindowDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(IDC_SEARCH, OnSearch) COMMAND_ID_HANDLER(IDOK, OnCloseCmd) COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) CHAIN_MSG_MAP(CDynamicDialogLayout) ALT_MSG_MAP(1) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONUP, OnMouseUp) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnMouseDown) END_MSG_MAP() // Handler prototypes (uncomment arguments if needed): // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) private: void ClearWindowDetails(); void SelectWindow(HWND hWnd); LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD id, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnSearch(WORD /*wNotifyCode*/, WORD id, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnMouseDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMouseUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); CContainedWindowT m_WinDrag; CIcon m_DragCursor; CWindow m_hCursorWnd; IMainFrame* m_pFrame; HWND m_SelectedHwnd{ nullptr }; bool m_Capture{ false }; }; ================================================ FILE: WinSpy/FormatHelper.cpp ================================================ #include "pch.h" #include "FormatHelper.h" #include CString FormatHelper::FormatHWndOrNone(HWND hWnd) { CString text; if (hWnd) text.Format(L"0x%zX", (DWORD_PTR)hWnd); else text = L"(None)"; return text; } CString FormatHelper::RectToString(CRect const& rc) { CString text; text.Format(L"(%d,%d)-(%d,%d) [%d x %d]", rc.left, rc.top, rc.right, rc.bottom, rc.Width(), rc.Height()); return text; } DWORD_PTR FormatHelper::ParseHex(CString const& text) { std::wstringstream ss; ss << std::hex; if (text.Left(2).CompareNoCase(L"0x") == 0) ss << (PCWSTR)text.Mid(2); else ss << (PCWSTR)text; DWORD_PTR value; ss >> value; return value; } CString FormatHelper::FormatPoint(POINT const& pt) { CString text; text.Format(L"(%d,%d)", pt.x, pt.y); return text; } ================================================ FILE: WinSpy/FormatHelper.h ================================================ #pragma once struct FormatHelper { static CString FormatHWndOrNone(HWND hWnd); static CString RectToString(CRect const& rc); static DWORD_PTR ParseHex(CString const& text); static CString FormatPoint(POINT const& pt); }; ================================================ FILE: WinSpy/FrameWindowHelper.h ================================================ #pragma once #include "Interfaces.h" template struct CFrameWindowHelper : CIdleHandler { HWND CreateAndInitToolBar(const ToolBarButtonInfo* buttons, int count) { auto pT = static_cast(this); CToolBarCtrl tb; auto hWndToolBar = tb.Create(pT->m_hWnd, CWindow::rcDefault, nullptr, ATL_SIMPLE_TOOLBAR_PANE_STYLE | TBSTYLE_LIST, 0, ATL_IDW_TOOLBAR); tb.SetExtendedStyle(TBSTYLE_EX_MIXEDBUTTONS); CImageList tbImages; tbImages.Create(24, 24, ILC_COLOR32 | ILC_COLOR | ILC_MASK, 4, 4); tb.SetImageList(tbImages); for (int i = 0; i < count; i++) { auto& b = buttons[i]; if (b.id == 0) tb.AddSeparator(0); else { int image = b.image == 0 ? I_IMAGENONE : tbImages.AddIcon(AtlLoadIconImage(b.image, 0, 24, 24)); tb.AddButton(b.id, b.style | (b.text ? BTNS_SHOWTEXT : 0), TBSTATE_ENABLED, image, b.text, 0); } } pT->CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE); pT->AddSimpleReBarBand(tb); pT->UIAddToolBar(hWndToolBar); _Module.GetMessageLoop()->AddIdleHandler(this); return hWndToolBar; } BOOL OnIdle() override { auto pT = static_cast(this); pT->UIUpdateToolBar(); return FALSE; } }; ================================================ FILE: WinSpy/HookHelper.cpp ================================================ #include "pch.h" #include "HookHelper.h" decltype(AddHook)* pAddHook; decltype(RemoveHook)* pRemoveHook; bool HookHelper::InitHookLib() { if (!pAddHook) { auto hLib = ::LoadLibrary(L"WinSpyHook.dll"); if (hLib) { pAddHook = (decltype(pAddHook))::GetProcAddress(hLib, "AddHook"); pRemoveHook = (decltype(pRemoveHook))::GetProcAddress(hLib, "RemoveHook"); } } return pAddHook != nullptr; } bool __stdcall HookHelper::AddHook(DWORD hookType, HookConfig const& config) { if (!InitHookLib()) return false; return pAddHook(hookType, config); } bool __stdcall HookHelper::RemoveHook(DWORD tid) { if (!InitHookLib()) return false; return pRemoveHook(tid); } ================================================ FILE: WinSpy/HookHelper.h ================================================ #pragma once #include "hooks.h" struct HookHelper abstract final { static bool InitHookLib(); static bool WINAPI AddHook(DWORD hookType, HookConfig const& config); static bool WINAPI RemoveHook(DWORD tid); }; ================================================ FILE: WinSpy/IconHelper.cpp ================================================ #include "pch.h" #include "IconHelper.h" HICON IconHelper::GetShieldIcon() { return GetStockIcon(SIID_SHIELD); } HICON IconHelper::GetStockIcon(SHSTOCKICONID id, bool big) { SHSTOCKICONINFO ssii = { sizeof(ssii) }; if (FAILED(::SHGetStockIconInfo(id, (big ? 0 : SHGSI_SMALLICON) | SHGSI_ICON, &ssii))) return nullptr; return ssii.hIcon; } ================================================ FILE: WinSpy/IconHelper.h ================================================ #pragma once struct IconHelper { static HICON GetStockIcon(SHSTOCKICONID id, bool big = false); static HICON GetShieldIcon(); }; ================================================ FILE: WinSpy/ImageIconCache.cpp ================================================ #include "pch.h" #include "ImageIconCache.h" #include "resource.h" int ImageIconCache::GetIcon(const CString& path, HICON* phIcon) const { if (path.IsEmpty() || path.Find(L'\\') < 0) return _defIcon; std::wstring wspath(path); { auto it = _icons.find(wspath); if (it != _icons.end()) { int index = it->second; if (phIcon) *phIcon = _images.GetIcon(index); return index; } } WORD index = 0; CString spath(path); auto hIcon = ::ExtractAssociatedIcon(_Module.GetModuleInstance(), spath.GetBufferSetLength(MAX_PATH), &index); if (hIcon) { int index = _images.AddIcon(hIcon); if (phIcon) *phIcon = hIcon; _icons.insert({ wspath, index }); return index; } return _defIcon; } ImageIconCache::Map::const_iterator ImageIconCache::begin() const { return _icons.begin(); } ImageIconCache::Map::const_iterator ImageIconCache::end() const { return _icons.end(); } ImageIconCache::ImageIconCache() { } void ImageIconCache::SetImageList(HIMAGELIST hil) { _images.Attach(hil); _defIcon = _images.AddIcon(AtlLoadSysIcon(IDI_APPLICATION)); } HIMAGELIST ImageIconCache::GetImageList() const { return _images; } ImageIconCache& ImageIconCache::Get() { static ImageIconCache cache; return cache; } ================================================ FILE: WinSpy/ImageIconCache.h ================================================ #pragma once struct ImageIconCache { static ImageIconCache& Get(); HIMAGELIST GetImageList() const; void SetImageList(HIMAGELIST hil); int GetIcon(const CString& path, HICON* phIcon = nullptr) const; using Map = std::unordered_map; Map::const_iterator begin() const; Map::const_iterator end() const; private: ImageIconCache(); ImageIconCache(const ImageIconCache&) = delete; ImageIconCache& operator=(const ImageIconCache&) = delete; private: mutable CImageList _images; mutable Map _icons; int _defIcon; }; ================================================ FILE: WinSpy/Interfaces.h ================================================ #pragma once struct CMessagesView; struct IMainFrame { virtual CUpdateUIBase& GetUIUpdate() = 0; virtual UINT ShowPopupMenu(HMENU hMenu, const POINT& pt, DWORD flags = 0) = 0; virtual HWND GetHwnd() const = 0; virtual CMessagesView* CreateMessagesView() = 0; virtual void CloseTab(CWindow* win) = 0; }; struct ToolBarButtonInfo { UINT id; int image; BYTE style = BTNS_BUTTON; PCWSTR text = nullptr; }; ================================================ FILE: WinSpy/MainFrm.cpp ================================================ // MainFrm.cpp : implmentation of the CMainFrame class // ///////////////////////////////////////////////////////////////////////////// #include "pch.h" #include "resource.h" #include "aboutdlg.h" #include "WindowsView.h" #include "MainFrm.h" #include "FindWindowDlg.h" #include "IconHelper.h" #include "ImageIconCache.h" #include "ProcessesView.h" #include "SecurityHelper.h" #include "MessagesView.h" #include "AutomationTreeView.h" const int WINDOW_MENU_POSITION = 5; BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { if (CFrameWindowImpl::PreTranslateMessage(pMsg)) return TRUE; return m_view.PreTranslateMessage(pMsg); } BOOL CMainFrame::OnIdle() { UIUpdateToolBar(); return FALSE; } LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if (SecurityHelper::IsRunningElevated()) { CMenuHandle menu = GetMenu(); menu.GetSubMenu(0).DeleteMenu(0, MF_BYPOSITION); menu.GetSubMenu(0).DeleteMenu(0, MF_BYPOSITION); } InitMenu(); CToolBarCtrl tb; auto hWndToolBar = tb.Create(m_hWnd, nullptr, nullptr, ATL_SIMPLE_TOOLBAR_PANE_STYLE | TBSTYLE_LIST, 0, ATL_IDW_TOOLBAR); tb.SetExtendedStyle(TBSTYLE_EX_MIXEDBUTTONS); InitToolBar(tb); CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE); AddSimpleReBarBand(hWndToolBar, nullptr, TRUE); CReBarCtrl rb(m_hWndToolBar); rb.LockBands(true); CreateSimpleStatusBar(); //m_view.m_bTabCloseButton = false; m_hWndClient = m_view.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0); UIAddToolBar(hWndToolBar); UISetCheck(ID_VIEW_TOOLBAR, 1); UISetCheck(ID_VIEW_STATUS_BAR, 1); ImageIconCache::Get().SetImageList(WindowHelper::GetImageList()); // register object for message filtering and idle updates CMessageLoop* pLoop = _Module.GetMessageLoop(); ATLASSERT(pLoop != NULL); pLoop->AddMessageFilter(this); pLoop->AddIdleHandler(this); CImageList images; images.Create(16, 16, ILC_COLOR32, 4, 4); UINT icons[] = { IDI_WINDOWS, IDI_PROCESSES, IDI_MESSAGES, IDI_AUTOMATION }; for (auto icon : icons) { images.AddIcon(AtlLoadIconImage(icon, 0, 16, 16)); } m_view.SetImageList(images); m_view.SetWindowMenu(((CMenuHandle)GetMenu()).GetSubMenu(WINDOW_MENU_POSITION)); PostMessage(WM_COMMAND, ID_VIEW_ALLWINDOWS); return 0; } LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { // unregister message filtering and idle updates CMessageLoop* pLoop = _Module.GetMessageLoop(); ATLASSERT(pLoop != NULL); pLoop->RemoveMessageFilter(this); pLoop->RemoveIdleHandler(this); bHandled = FALSE; return 1; } LRESULT CMainFrame::OnMenuSelect(UINT, WPARAM, LPARAM, BOOL&) { return 0; } LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { PostMessage(WM_CLOSE); return 0; } LRESULT CMainFrame::OnViewAllWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { auto pView = new CWindowsView(this); { CWaitCursor wait; pView->Create(m_view, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0); m_view.AddPage(pView->m_hWnd, _T("Windows Tree"), 0, pView); } pView->OnActivate(true); return 0; } LRESULT CMainFrame::OnViewAllProcesses(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { auto pView = new CProcessesView(this); { CWaitCursor wait; pView->Create(m_view, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0); m_view.AddPage(pView->m_hWnd, _T("Processes"), 1, pView); } pView->OnActivate(true); return 0; } LRESULT CMainFrame::OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { static BOOL bVisible = TRUE; // initially visible bVisible = !bVisible; CReBarCtrl rebar = m_hWndToolBar; int nBandIndex = rebar.IdToIndex(ATL_IDW_BAND_FIRST + 1); // toolbar is 2nd added band rebar.ShowBand(nBandIndex, bVisible); UISetCheck(ID_VIEW_TOOLBAR, bVisible); UpdateLayout(); return 0; } LRESULT CMainFrame::OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { BOOL bVisible = !::IsWindowVisible(m_hWndStatusBar); ::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE); UISetCheck(ID_VIEW_STATUS_BAR, bVisible); UpdateLayout(); return 0; } LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { CAboutDlg dlg; dlg.DoModal(); return 0; } LRESULT CMainFrame::OnWindowClose(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { int nActivePage = m_view.GetActivePage(); if (nActivePage != -1) m_view.RemovePage(nActivePage); else ::MessageBeep((UINT)-1); return 0; } LRESULT CMainFrame::OnWindowCloseAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { m_view.RemoveAllPages(); return 0; } LRESULT CMainFrame::OnWindowActivate(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { int nPage = wID - ID_WINDOW_TABFIRST; m_view.SetActivePage(nPage); return 0; } LRESULT CMainFrame::OnCommandToActiveView(WORD code, WORD id, HWND h, BOOL&) { auto page = m_view.GetActivePage(); if (page < 0) return 0; return ::SendMessage(m_view.GetPageHWND(page), WM_COMMAND, MAKELONG(id, code), reinterpret_cast(h)); } LRESULT CMainFrame::OnFindWindow(WORD, WORD, HWND, BOOL&) { CFindWindowDlg dlg(this); if (IDOK == dlg.DoModal()) { WindowHelper::ShowWindowProperties(dlg.GetSelectedHwnd()); } return 0; } LRESULT CMainFrame::OnRunAsAdmin(WORD, WORD, HWND, BOOL&) { if (SecurityHelper::RunElevated()) { SendMessage(WM_CLOSE); } return 0; } LRESULT CMainFrame::OnTabCloseButton(int, LPNMHDR, BOOL&) { return 0; } LRESULT CMainFrame::OnViewAutomationTree(WORD, WORD, HWND, BOOL&) { auto pView = new CAutomationTreeView(this); pView->Create(m_view, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0); m_view.AddPage(pView->m_hWnd, L"UI Automation", 2, pView); pView->OnActivate(true); return 0; } CUpdateUIBase& CMainFrame::GetUIUpdate() { return *this; } UINT CMainFrame::ShowPopupMenu(HMENU hMenu, const POINT& pt, DWORD flags) { return (UINT)ShowContextMenu(hMenu, 0, pt.x, pt.y); } CMessagesView* CMainFrame::CreateMessagesView() { auto pView = new CMessagesView(this); pView->Create(m_view, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0); m_view.AddPage(pView->m_hWnd, L"Messages", 2, pView); pView->OnActivate(true); return pView; } void CMainFrame::CloseTab(CWindow* win) { m_view.RemovePage(m_view.GetActivePage()); } void CMainFrame::InitToolBar(CToolBarCtrl& tb) { CImageList tbImages; tbImages.Create(24, 24, ILC_COLOR32, 8, 4); tb.SetImageList(tbImages); struct { UINT id; int image; int style = BTNS_BUTTON; PCWSTR text = nullptr; } buttons[] = { { ID_VIEW_REFRESH, IDI_REFRESH }, { 0 }, { ID_VIEW_ALLWINDOWS, IDI_WINDOWS }, { ID_VIEW_ALLPROCESSES, IDI_PROCESSES }, { ID_WINDOW_FIND, IDI_WINDOWSEARCH }, { 0 }, { ID_VIEW_HIDDENWINDOWS, IDI_WINDOW_HIDDEN }, { ID_VIEW_EMPTYTITLEWINDOWS, IDI_WINDOW_NOTEXT }, { ID_WINDOW_PROPERTIES, IDI_WINPROP }, }; for (auto& b : buttons) { if (b.id == 0) tb.AddSeparator(0); else { int image = tbImages.AddIcon(AtlLoadIconImage(b.image, 0, 24, 24)); tb.AddButton(b.id, b.style, TBSTATE_ENABLED, image, b.text, 0); } } } void CMainFrame::InitMenu() { AddMenu(GetMenu()); struct { UINT id, icon; HICON hIcon = nullptr; } cmds[] = { { ID_FILE_RUNASADMINISTRATOR, 0, IconHelper::GetShieldIcon() }, { ID_VIEW_REFRESH, IDI_REFRESH }, { ID_VIEW_ALLWINDOWS, IDI_WINDOWS }, { ID_VIEW_HIDDENWINDOWS, IDI_WINDOW_HIDDEN }, { ID_VIEW_EMPTYTITLEWINDOWS, IDI_WINDOW_NOTEXT }, { ID_WINDOW_CLOSE, IDI_WINDOW_CLOSE }, { ID_STATE_CLOSE, IDI_WINDOW_CLOSE }, { ID_WINDOW_MINIMIZE, IDI_WINDOW_MINIMIZE }, { ID_WINDOW_MAXIMIZE, IDI_WINDOW_MAXIMIZE }, { ID_VIEW_ALLPROCESSES, IDI_PROCESSES }, { ID_WINDOW_PROPERTIES, IDI_WINPROP }, { ID_PROCESS_PROPERTIES, IDI_PROCESS_INFO }, { ID_WINDOW_FIND, IDI_WINDOWSEARCH }, { ID_WINDOW_RESTORE, IDI_RESTORE }, { ID_TREE_SENDTOBACK, IDI_WINDOW_SENDTOBACK }, { ID_WINDOW_BRINGTOFRONT, IDI_SENDTOFRONT }, }; for (auto& cmd : cmds) { if (cmd.icon) AddCommand(cmd.id, cmd.icon); else AddCommand(cmd.id, cmd.hIcon); } } LRESULT CMainFrame::OnTabActivated(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { if (m_ActivePage >= 0 && m_ActivePage < m_view.GetPageCount()) ::SendMessage(m_view.GetPageHWND(m_ActivePage), TBVN_PAGEACTIVATED, 0, 0); m_ActivePage = m_view.GetActivePage(); if (m_ActivePage < 0) return 0; return ::SendMessage(m_view.GetPageHWND(m_ActivePage), TBVN_PAGEACTIVATED, 1, 0); } ================================================ FILE: WinSpy/MainFrm.h ================================================ // MainFrm.h : interface of the CMainFrame class // ///////////////////////////////////////////////////////////////////////////// #pragma once #include "Interfaces.h" #include #include class CMainFrame : public CFrameWindowImpl, public CAutoUpdateUI, public COwnerDrawnMenu, public CMessageFilter, public CIdleHandler, public IMainFrame { public: DECLARE_FRAME_WND_CLASS(L"WinSpyMainWindowClass", IDR_MAINFRAME) BOOL PreTranslateMessage(MSG* pMsg) override; BOOL OnIdle() override; BEGIN_MSG_MAP(CMainFrame) //NOTIFY_CODE_HANDLER(TBVN_TABCLOSEBTN, OnTabCloseButton) COMMAND_ID_HANDLER(ID_VIEW_ALLWINDOWS, OnViewAllWindows) COMMAND_ID_HANDLER(ID_VIEW_ALLPROCESSES, OnViewAllProcesses) COMMAND_ID_HANDLER(ID_VIEW_AUTOMATIONTREE, OnViewAutomationTree) COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar) COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar) COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) COMMAND_ID_HANDLER(ID_WINDOW_FIND, OnFindWindow) COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose) COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll) COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate) COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) COMMAND_ID_HANDLER(ID_FILE_RUNASADMINISTRATOR, OnRunAsAdmin) MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) CHAIN_MSG_MAP(CAutoUpdateUI) CHAIN_MSG_MAP(CFrameWindowImpl) COMMAND_RANGE_HANDLER(1, 0xffff, OnCommandToActiveView) NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabActivated) CHAIN_MSG_MAP(COwnerDrawnMenu) END_MSG_MAP() private: // IMainFrame CUpdateUIBase& GetUIUpdate() override; UINT ShowPopupMenu(HMENU hMenu, const POINT& pt, DWORD flags = 0) override; HWND GetHwnd() const override { return m_hWnd; } CMessagesView* CreateMessagesView() override; void CloseTab(CWindow* win) override; void InitToolBar(CToolBarCtrl& tb); void InitMenu(); // Handler prototypes (uncomment arguments if needed): // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnTabActivated(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); LRESULT OnMenuSelect(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnViewAllWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnViewAllProcesses(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowClose(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowCloseAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowActivate(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnCommandToActiveView(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnFindWindow(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnRunAsAdmin(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnTabCloseButton(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnViewAutomationTree(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); int m_ActivePage = -1; CCustomTabView m_view; }; ================================================ FILE: WinSpy/MessageDecoder.cpp ================================================ #include "pch.h" #include "MessageDecoder.h" #include "FormatHelper.h" #define CASE_STR(x) case x: return L#x CString MessageDecoder::Decode(UINT msg, WPARAM wp, LPARAM lp) { if (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) { CPoint pt(GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return L"Keys: [" + MouseKey((int)wp) + L"] Pos: " + FormatHelper::FormatPoint(pt); } if (msg >= WM_KEYFIRST && msg <= WM_KEYLAST) { CString text; text.Format(L"VK: %u", (DWORD)wp); return text; } switch (msg) { case WM_SYSCOMMAND: { CPoint pt(GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); auto cmd = SysCommandToString((DWORD)wp); CString pos; if (pt.y > 1) { pos = L"Cursor: " + FormatHelper::FormatPoint(pt); } return L"Cmd: " + cmd + L" " + pos; } case WM_SETREDRAW: return CString(L"Redraw: ") + (wp ? L"True" : L"False"); case WM_SIZE: { CPoint pt(GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); auto size = L" Size: " + FormatHelper::FormatPoint(pt); return L"Type: " + SizeParamToString((DWORD)wp) + size; } } return L""; } CString MessageDecoder::MouseKey(int key) { static const struct { int key; PCWSTR name; } keys[] = { { MK_SHIFT, L"Shift" }, { MK_CONTROL, L"Ctrl" }, { MK_LBUTTON, L"LButton" }, { MK_RBUTTON, L"RButton" }, { MK_MBUTTON, L"MButton" }, { MK_XBUTTON1, L"XButton1" }, { MK_XBUTTON2, L"XButton2" }, }; CString text; for (auto& item : keys) { if ((key & item.key) == item.key) text += item.name + CString(L", "); } if (!text.IsEmpty()) text = text.Left(text.GetLength() - 2); else text = L"None"; return text; } CString MessageDecoder::SysCommandToString(DWORD cmd) { switch (cmd) { CASE_STR(SC_CLOSE); CASE_STR(SC_CONTEXTHELP); CASE_STR(SC_DEFAULT); CASE_STR(SC_HOTKEY); CASE_STR(SC_HSCROLL); CASE_STR(SCF_ISSECURE); CASE_STR(SC_KEYMENU); CASE_STR(SC_MAXIMIZE); CASE_STR(SC_MINIMIZE); CASE_STR(SC_MONITORPOWER); CASE_STR(SC_MOUSEMENU); CASE_STR(SC_MOVE); CASE_STR(SC_NEXTWINDOW); CASE_STR(SC_PREVWINDOW); CASE_STR(SC_RESTORE); CASE_STR(SC_SCREENSAVE); CASE_STR(SC_SIZE); CASE_STR(SC_TASKLIST); CASE_STR(SC_VSCROLL); } CString text; text.Format(L"0x%X", cmd); return text; } CString MessageDecoder::SizeParamToString(DWORD type) { switch (type) { CASE_STR(SIZE_MAXHIDE); CASE_STR(SIZE_MAXIMIZED); CASE_STR(SIZE_MAXSHOW); CASE_STR(SIZE_MINIMIZED); CASE_STR(SIZE_RESTORED); } CString text; text.Format(L"%u", type); return text; } ================================================ FILE: WinSpy/MessageDecoder.h ================================================ #pragma once struct MessageDecoder final abstract { static CString Decode(UINT msg, WPARAM wp, LPARAM lp); static CString MouseKey(int key); static CString SysCommandToString(DWORD cmd); static CString SizeParamToString(DWORD type); }; ================================================ FILE: WinSpy/MessagesView.cpp ================================================ #include "pch.h" #include "MessagesView.h" #include "hooks.h" #include "FormatHelper.h" #include "WindowHelper.h" #include "MessageDecoder.h" #include "HookHelper.h" bool CMessagesView::CaptureWindow(HWND hWnd) { if (!m_hReadyEvent) m_hReadyEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); m_CaptureHwnd = hWnd; m_CaptureOptions = CaptureOptions::Window; m_CaptureTid = ::GetWindowThreadProcessId(hWnd, nullptr); if (m_hThread) { ::PostThreadMessage(::GetThreadId(m_hThread), WM_QUIT, 0, 0); ::CloseHandle(m_hThread); } m_hThread = ::CreateThread(nullptr, 0, [](auto param) { return ((CMessagesView*)param)->ProcessHook(); }, this, 0, nullptr); if (!m_hThread) return false; ATLVERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hReadyEvent, INFINITE)); ATLASSERT(m_CallbackWnd); HookConfig config{}; config.CallbackWnd = m_CallbackWnd; config.TargetWnd = hWnd; config.Options = HookOptions::Window | HookOptions::ChildWindows; config.ThreadId = m_CaptureTid; return HookHelper::AddHook(WH_GETMESSAGE, config); } void CMessagesView::HookCallbackMsg(DWORD hookType, HookDataHeader* header) { switch (header->HookType) { case WH_GETMESSAGE: auto data = reinterpret_cast(header); if (data->wParam == PM_REMOVE) { MessageInfo mi(data->Msg); mi.ThreadId = ::GetWindowThreadProcessId(mi.hwnd, &mi.ProcessId); std::lock_guard locker(m_TempMessagesLock); m_TempMessages.push_back(mi); } break; } } void CMessagesView::OnFinalMessage(HWND) { delete this; } CString CMessagesView::GetColumnText(HWND hWnd, int row, int col) const { auto& item = m_Messages[row]; CString text; switch (GetColumnManager(hWnd)->GetColumnTag(col)) { case ColumnType::Window: return FormatHelper::FormatHWndOrNone(item.hwnd); case ColumnType::Message: text.Format(L"%s (0x%X)", (PCWSTR)WindowHelper::WindowMessageToString(item.message), item.message); break; case ColumnType::Thread: text.Format(L"%u", item.ThreadId); break; case ColumnType::Process: text.Format(L"%u", item.ProcessId); break; case ColumnType::wParam: text.Format(L"0x%zX", item.wParam); break; case ColumnType::lParam: text.Format(L"0x%zX", item.lParam); break; case ColumnType::Time: text.Format(L"0x%X", item.time); break; case ColumnType::Point: return FormatHelper::FormatPoint(item.pt); case ColumnType::DecodedMessage: return MessageDecoder::Decode(item.message, item.wParam, item.lParam); } return text; } void CMessagesView::UpdateList() { m_List.SetItemCountEx((int)m_Messages.size(), LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL); m_List.RedrawItems(m_List.GetTopIndex(), m_List.GetTopIndex() + m_List.GetCountPerPage()); } DWORD CMessagesView::ProcessHook() { m_CallbackWnd.Create(HWND_MESSAGE, nullptr, nullptr); ATLASSERT(m_CallbackWnd); ::SetEvent(m_hReadyEvent); MSG msg; while (::GetMessage(&msg, nullptr, 0, 0)) ::DispatchMessage(&msg); m_CallbackWnd.DestroyWindow(); return 0; } LRESULT CMessagesView::OnCreate(UINT, WPARAM, LPARAM, BOOL&) { m_hWndClient = m_List.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | LVS_SHAREIMAGELISTS, WS_EX_CLIENTEDGE); m_List.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP); auto cm = GetColumnManager(m_List); struct { PCWSTR text; ColumnType type; int width = 100; int format = LVCFMT_LEFT; ColumnFlags flags = ColumnFlags::Visible; } columns[] = { // { L"Type", ColumnType::Type, 70 }, { L"Time", ColumnType::Time, 120, LVCFMT_LEFT }, { L"Window", ColumnType::Window, 100, LVCFMT_RIGHT }, { L"Message", ColumnType::Message, 140, LVCFMT_LEFT }, { L"WPARAM", ColumnType::wParam, 100, LVCFMT_RIGHT }, { L"LPARAM", ColumnType::lParam, 100, LVCFMT_RIGHT }, { L"Thread", ColumnType::Thread, 100, LVCFMT_RIGHT }, { L"Decoded Message", ColumnType::DecodedMessage, 250 }, { L"Process", ColumnType::Process, 100, LVCFMT_RIGHT }, { L"Point", ColumnType::Point, 100, LVCFMT_RIGHT }, }; for (auto& col : columns) { cm->AddColumn(col.text, col.format, col.width, col.type, col.flags); } cm->UpdateColumns(); SetTimer(1, 1200); return 0; } LRESULT CMessagesView::OnTimer(UINT, WPARAM id, LPARAM, BOOL&) { if (id == 1) { { std::lock_guard locker(m_TempMessagesLock); if (m_TempMessages.empty()) return 0; m_Messages.insert(m_Messages.end(), m_TempMessages.begin(), m_TempMessages.end()); m_TempMessages.clear(); } UpdateList(); } return 0; } LRESULT CMessagesView::OnDestroy(UINT, WPARAM, LPARAM, BOOL&) { if (m_hThread) { ::PostThreadMessage(::GetThreadId(m_hThread), WM_QUIT, 0, 0); ::CloseHandle(m_hThread); HookHelper::RemoveHook(m_CaptureTid); } if (m_hReadyEvent) ::CloseHandle(m_hReadyEvent); return LRESULT(); } LRESULT CHookCallbackWnd::OnHookCallback(UINT msg, WPARAM wp, LPARAM lp, BOOL&) { auto cds = reinterpret_cast(lp); auto header = reinterpret_cast(cds->lpData); m_pView->HookCallbackMsg(header->HookType, header); return TRUE; } ================================================ FILE: WinSpy/MessagesView.h ================================================ #pragma once #include "ViewBase.h" #include "VirtualListView.h" #include "hooks.h" struct CMessagesView; struct CHookCallbackWnd : CWindowImpl { CHookCallbackWnd(CMessagesView* pView) : m_pView(pView) {} BEGIN_MSG_MAP(CHookCallbackWnd) MESSAGE_HANDLER(WM_COPYDATA, OnHookCallback) END_MSG_MAP() LRESULT OnHookCallback(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); private: CMessagesView* m_pView; }; struct CMessagesView : CViewBase, CVirtualListView { CMessagesView(IMainFrame* frame) : CViewBase(frame), m_CallbackWnd(this) {} bool CaptureWindow(HWND hWnd); bool CaptureThread(DWORD tid); bool CaptureProcess(DWORD tid); void HookCallbackMsg(DWORD hookType, HookDataHeader* header); void OnFinalMessage(HWND) override; CString GetColumnText(HWND, int row, int col) const; protected: BEGIN_MSG_MAP(CMessagesView) MESSAGE_HANDLER(WM_TIMER, OnTimer) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) CHAIN_MSG_MAP(CVirtualListView) CHAIN_MSG_MAP(CViewBase) END_MSG_MAP() private: enum class ColumnType { Type, Window, Thread, Process, Message, DecodedMessage, wParam, lParam, Time, Point }; enum class CaptureOptions { Window, Thread, Process }; enum class MessageInfoFlags { None = 0, Result = 1, }; struct MessageInfo : MSG { MessageInfoFlags Flags; DWORD ThreadId; DWORD ProcessId; }; void UpdateUI(); void UpdateList(); DWORD ProcessHook(); LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); CListViewCtrl m_List; std::vector m_Messages; std::vector m_TempMessages; std::mutex m_TempMessagesLock; CaptureOptions m_CaptureOptions; DWORD m_CaptureTid; HWND m_CaptureHwnd; HANDLE m_hThread{ nullptr }; CHookCallbackWnd m_CallbackWnd; HANDLE m_hReadyEvent{ nullptr }; }; ================================================ FILE: WinSpy/ProcessHelper.cpp ================================================ #include "pch.h" #include "ProcessHelper.h" #include "WindowHelper.h" #include #include CString ProcessHelper::GetProcessImageName(DWORD pid, bool fullPath) { if (_names.empty()) EnumProcesses(); auto hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); CString result; if (hProcess) { WCHAR name[MAX_PATH]; DWORD size = _countof(name); if (::QueryFullProcessImageName(hProcess, 0, name, &size)) { result = fullPath ? name : ::wcsrchr(name, L'\\') + 1; } ::CloseHandle(hProcess); } else if (auto it = _names.find(pid); it != _names.end()) return it->second; return result; } ProcessesInfo ProcessHelper::EnumProcessesAndThreads(EnumProcessesOptions options) { ProcessesInfo info; auto& processes = info.Processes; processes.reserve(512); auto hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return info; PROCESSENTRY32 pe; pe.dwSize = sizeof(pe); std::unordered_map processMap; processMap.reserve(512); ::Process32First(hSnapshot, &pe); while (::Process32Next(hSnapshot, &pe)) { ProcessInfo pi; pi.ProcessId = pe.th32ProcessID; pi.Threads.reserve(pe.cntThreads); pi.ProcessName = pe.szExeFile; auto hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pi.ProcessId); if (hProcess) { DWORD size = MAX_PATH; ::QueryFullProcessImageName(hProcess, 0, pi.FullPath.GetBufferSetLength(size), &size); ::CloseHandle(hProcess); } processes.push_back(pi); processMap.insert({ pi.ProcessId, (int)processes.size() - 1 }); } std::unordered_set msgOnly; if ((options & EnumProcessesOptions::IncludeMessageOnly) == EnumProcessesOptions::IncludeMessageOnly) { HWND hWnd = nullptr; for (;;) { hWnd = ::FindWindowEx(HWND_MESSAGE, hWnd, nullptr, nullptr); if (!hWnd) break; auto tid = ::GetWindowThreadProcessId(hWnd, nullptr); msgOnly.insert(tid); if (auto it = info.MessageOnly.find(tid); it != info.MessageOnly.end()) { it->second.push_back(hWnd); } else { info.MessageOnly.insert({ tid, { hWnd } }); } } } THREADENTRY32 te; te.dwSize = sizeof(te); ::Thread32First(hSnapshot, &te); do { // // skip idle process // if (te.th32OwnerProcessID == 0) continue; auto& pi = processes[processMap[te.th32OwnerProcessID]]; if ((options & EnumProcessesOptions::UIThreadsOnly) == EnumProcessesOptions::UIThreadsOnly) { if (!WindowHelper::ThreadHasWindows(te.th32ThreadID) && !msgOnly.contains(te.th32ThreadID)) continue; } pi.Threads.push_back(te.th32ThreadID); } while (::Thread32Next(hSnapshot, &te)); if ((options & EnumProcessesOptions::SkipProcessesWithNoUI) == EnumProcessesOptions::SkipProcessesWithNoUI) { for (int i = 0; i < (int)processes.size(); i++) { if (processes[i].Threads.empty()) { processes.erase(processes.begin() + i); i--; } } } return info; } void ProcessHelper::ShowProcessProperties(ProcessInfo const& pi) { } void ProcessHelper::EnumProcesses() { auto hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); ATLASSERT(hSnapshot != INVALID_HANDLE_VALUE); if (hSnapshot == INVALID_HANDLE_VALUE) { _names.insert({ 0, L"(Idle)" }); return; } PROCESSENTRY32 pe; pe.dwSize = sizeof(pe); ::Process32First(hSnapshot, &pe); _names.reserve(512); while (::Process32Next(hSnapshot, &pe)) { auto hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID); if (!hProcess) { _names.insert({ pe.th32ProcessID, pe.szExeFile }); } else { ::CloseHandle(hProcess); } } } ================================================ FILE: WinSpy/ProcessHelper.h ================================================ #pragma once struct ProcessInfo { DWORD ProcessId; std::vector Threads; CString ProcessName; CString FullPath; }; enum class EnumProcessesOptions { None = 0, UIThreadsOnly = 1, SkipProcessesWithNoUI = 2, IncludeMessageOnly = 4, }; DEFINE_ENUM_FLAG_OPERATORS(EnumProcessesOptions); struct ProcessesInfo { std::vector Processes; std::unordered_map> MessageOnly; }; struct ProcessHelper { static CString GetProcessImageName(DWORD pid, bool fullPath = false); static ProcessesInfo EnumProcessesAndThreads(EnumProcessesOptions options); static void ShowProcessProperties(ProcessInfo const& pi); static void EnumProcesses(); private: inline static std::unordered_map _names; }; ================================================ FILE: WinSpy/ProcessesView.cpp ================================================ #include "pch.h" #include "resource.h" #include "ProcessesView.h" #include "ImageIconCache.h" LRESULT CProcessesView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { m_Splitter.SetSplitterExtendedStyle(SPLIT_FLATBAR | SPLIT_PROPORTIONAL); m_hWndClient = m_Splitter.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); m_Tree.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS, 0, IDC_TREE); m_WindowsView.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); m_Tree.SetExtendedStyle(TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER); m_Tree.SetImageList(WindowHelper::GetImageList(), TVSIL_NORMAL); m_Splitter.SetSplitterPanes(m_Tree, m_WindowsView); UpdateLayout(); m_Splitter.SetSplitterPosPct(35); InitTree(); return 0; } void CProcessesView::OnActivate(bool active) { if (active) { UpdateUI(); } } LRESULT CProcessesView::OnTimer(UINT /*uMsg*/, WPARAM id, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if (id == 3) { KillTimer(3); ChangeSelection(m_Tree.GetSelectedItem()); } return 0; } void CProcessesView::InitTree() { m_Tree.LockWindowUpdate(); m_Tree.DeleteAllItems(); m_Processes.clear(); m_WindowMap.clear(); m_Threads.clear(); m_TotalWindows = m_TotalVisibleWindows = m_TopLevelWindows = 0; CString text; auto info = ProcessHelper::EnumProcessesAndThreads( EnumProcessesOptions::UIThreadsOnly | EnumProcessesOptions::SkipProcessesWithNoUI | EnumProcessesOptions::IncludeMessageOnly); for (auto& pi : info.Processes) { auto icon = ImageIconCache::Get().GetIcon(pi.FullPath); text.Format(L"%s (%u)", (PCWSTR)pi.ProcessName, pi.ProcessId); auto node = m_Tree.InsertItem(text, icon, icon, TVI_ROOT, TVI_SORT); node.SetData((DWORD_PTR)ItemType::Process); m_Processes.insert({ node, pi }); for (auto tid : pi.Threads) { text.Format(L"Thread %u (0x%X)", tid, tid); auto tnode = m_Tree.InsertItem(text, icon, icon, node, TVI_LAST); if (!WindowHelper::ThreadHasWindows(tid)) tnode.SetState(TVIS_CUT, TVIS_CUT); tnode.SetData((DWORD_PTR)ItemType::Thread); m_Threads.insert({ tnode, tid }); struct LocalData { CProcessesView* pThis; HTREEITEM hParent; }; LocalData data = { this, tnode }; ::EnumThreadWindows(tid, [](auto hWnd, auto param) { auto data = (LocalData*)param; data->pThis->AddNode(hWnd, data->hParent); return TRUE; }, reinterpret_cast(&data)); // // add message-only windows // if (auto it = info.MessageOnly.find(tid); it != info.MessageOnly.end()) { for (auto hWnd : it->second) { AddNode(hWnd, tnode); } } } } m_Tree.LockWindowUpdate(FALSE); } CTreeItem CProcessesView::AddNode(HWND hWnd, HTREEITEM hParent) { CString text, name; CWindow win(hWnd); m_TotalWindows++; if (win.IsWindowVisible()) m_TotalVisibleWindows++; m_TopLevelWindows++; if (!m_ShowHiddenWindows && !win.IsWindowVisible()) return nullptr; win.GetWindowText(name); if (!m_ShowNoTitleWindows && name.IsEmpty()) return nullptr; if (name.GetLength() > 64) name = name.Left(64) + L"..."; if (!name.IsEmpty()) name = L"[" + name + L"]"; WCHAR className[64] = { 0 }; ::GetClassName(hWnd, className, _countof(className)); text.Format(L"0x%zX (%s) %s", (DWORD_PTR)hWnd, className, (PCWSTR)name); HICON hIcon{ nullptr }; int image = 0; if ((win.GetStyle() & WS_CHILD) == 0) { auto& icons = WindowHelper::GetIconMap(); if (auto it = icons.find(hWnd); it == icons.end()) { hIcon = WindowHelper::GetWindowOrProcessIcon(hWnd); if (hIcon) { icons.insert({ hWnd, image = WindowHelper::GetImageList().AddIcon(hIcon) }); } } else { image = it->second; } } auto node = m_Tree.InsertItem(text, image, image, hParent, TVI_LAST); node.SetData((DWORD_PTR)hWnd); m_WindowMap.insert({ hWnd, node }); if (!win.IsWindowVisible()) node.SetState(TVIS_CUT, TVIS_CUT); if (win.GetWindow(GW_CHILD)) { // add a "plus" button node.AddTail(L"*", 0); } return node; } LRESULT CProcessesView::OnNodeExpanding(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; if (tv->action == TVE_EXPAND) { auto hItem = tv->itemNew.hItem; auto child = m_Tree.GetChildItem(hItem); if (child.GetData() == 0) { child.Delete(); AddChildWindows(hItem); } } return 0; } void CProcessesView::AddChildWindows(HTREEITEM hParent) { auto hWnd = (HWND)m_Tree.GetItemData(hParent); ATLASSERT(hWnd); m_hCurrentNode = hParent; ::EnumChildWindows(hWnd, [](auto hChild, auto p) -> BOOL { auto pThis = (CProcessesView*)p; return pThis->AddChildNode(hChild); }, reinterpret_cast(this)); } BOOL CProcessesView::AddChildNode(HWND hChild) { if (::GetAncestor(hChild, GA_PARENT) == (HWND)m_Tree.GetItemData(m_hCurrentNode)) { AddNode(hChild, m_hCurrentNode); } return TRUE; } LRESULT CProcessesView::OnTreeNodeRightClick(HTREEITEM hItem, CPoint const& pt) { ATLASSERT(m_Selected); auto data = static_cast(m_Selected.GetData()); int index = 0; if (data == ItemType::Thread) index = 3; else if (data == ItemType::Process) index = 2; CMenu menu; menu.LoadMenu(IDR_CONTEXT); return GetFrame()->ShowPopupMenu(menu.GetSubMenu(index), pt); } LRESULT CProcessesView::OnWindowShow(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); m_SelectedHwnd.ShowWindow(SW_SHOW); return 0; } LRESULT CProcessesView::OnWindowHide(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); m_SelectedHwnd.ShowWindow(SW_HIDE); return 0; } LRESULT CProcessesView::OnWindowMinimize(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); m_SelectedHwnd.ShowWindow(SW_MINIMIZE); return 0; } LRESULT CProcessesView::OnWindowMaximize(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); m_SelectedHwnd.ShowWindow(SW_MAXIMIZE); return 0; } LRESULT CProcessesView::OnWindowRestore(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); m_SelectedHwnd.ShowWindow(SW_RESTORE); return 0; } LRESULT CProcessesView::OnWindowFlash(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); WindowHelper::Flash((HWND)m_Selected.GetData()); return 0; } LRESULT CProcessesView::OnWindowBringToFront(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_Selected); m_SelectedHwnd.BringWindowToTop(); return 0; } LRESULT CProcessesView::OnNodeSelected(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; if (tv->action == TVC_BYKEYBOARD) { // short delay before update in case the user moves quickly through the tree SetTimer(3, 250, nullptr); } else { ChangeSelection(tv->itemNew.hItem); } return 0; } LRESULT CProcessesView::OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt) { auto data = m_Tree.GetItemData(hItem); switch (static_cast(data)) { case ItemType::Process: ProcessHelper::ShowProcessProperties(m_Processes[hItem]); break; case ItemType::Thread: break; default: auto hWnd = reinterpret_cast(data); WindowHelper::ShowWindowProperties(hWnd); break; } return 0; } LRESULT CProcessesView::OnWindowProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ATLASSERT(m_SelectedHwnd); WindowHelper::ShowWindowProperties(m_SelectedHwnd); return 0; } LRESULT CProcessesView::OnProcessProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { auto hItem = m_Tree.GetSelectedItem(); ProcessHelper::ShowProcessProperties(m_Processes[hItem]); return 0; } void CProcessesView::ChangeSelection(HTREEITEM hItem) { m_Selected = hItem; m_Selected.m_pTreeView = &m_Tree; auto data = static_cast(m_Selected.GetData()); switch (data) { case ItemType::Process: m_WindowsView.UpdateListByProcess(m_Processes[m_Selected]); break; case ItemType::Thread: m_WindowsView.UpdateListByThread(m_Threads[m_Selected]); break; default: auto hWnd = (HWND)m_Selected.GetData(); m_SelectedHwnd = hWnd; if (!::IsWindow(hWnd)) // window is probably destroyed m_Selected.Delete(); else { m_WindowsView.UpdateList(hWnd); } break; } } LRESULT CProcessesView::OnToggleHiddenWindows(WORD, WORD, HWND, BOOL&) { m_ShowHiddenWindows = !m_ShowHiddenWindows; UpdateUI(); InitTree(); return 0; } LRESULT CProcessesView::OnToggleEmptyTitleWindows(WORD, WORD, HWND, BOOL&) { m_ShowNoTitleWindows = !m_ShowNoTitleWindows; UpdateUI(); InitTree(); return 0; } void CProcessesView::UpdateUI() { auto& ui = GetFrame()->GetUIUpdate(); if (::GetFocus() == m_Tree) { ui.UISetCheck(ID_VIEW_HIDDENWINDOWS, m_ShowHiddenWindows); ui.UISetCheck(ID_VIEW_EMPTYTITLEWINDOWS, m_ShowNoTitleWindows); ui.UIEnable(ID_WINDOW_PROPERTIES, m_SelectedHwnd != nullptr); } else { m_WindowsView.UpdateUI(ui); } } ================================================ FILE: WinSpy/ProcessesView.h ================================================ #pragma once #include "ViewBase.h" #include "WindowsListView.h" #include "TreeViewManager.h" #include "ProcessHelper.h" class CProcessesView : public CViewBase, public CTreeViewManager { public: CProcessesView(IMainFrame* frame) : CViewBase(frame), m_WindowsView(frame) {} void OnActivate(bool active); protected: enum { IDC_TREE = 123 }; BEGIN_MSG_MAP(CProcessesView) MESSAGE_HANDLER(WM_TIMER, OnTimer) MESSAGE_HANDLER(WM_CREATE, OnCreate) NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING, OnNodeExpanding) //NOTIFY_CODE_HANDLER(TVN_DELETEITEM, OnNodeDeleted) COMMAND_ID_HANDLER(ID_VIEW_HIDDENWINDOWS, OnToggleHiddenWindows) COMMAND_ID_HANDLER(ID_VIEW_EMPTYTITLEWINDOWS, OnToggleEmptyTitleWindows) NOTIFY_CODE_HANDLER(TVN_SELCHANGED, OnNodeSelected) COMMAND_ID_HANDLER(ID_WINDOW_SHOW, OnWindowShow) COMMAND_ID_HANDLER(ID_WINDOW_HIDE, OnWindowHide) COMMAND_ID_HANDLER(ID_WINDOW_BRINGTOFRONT, OnWindowBringToFront) COMMAND_ID_HANDLER(ID_WINDOW_MINIMIZE, OnWindowMinimize) COMMAND_ID_HANDLER(ID_WINDOW_MAXIMIZE, OnWindowMaximize) COMMAND_ID_HANDLER(ID_STATE_FLASH, OnWindowFlash) COMMAND_ID_HANDLER(ID_WINDOW_RESTORE, OnWindowRestore) COMMAND_ID_HANDLER(ID_WINDOW_PROPERTIES, OnWindowProperties) COMMAND_ID_HANDLER(ID_PROCESS_PROPERTIES, OnProcessProperties) CHAIN_MSG_MAP(CTreeViewManager) CHAIN_MSG_MAP(CViewBase) if (m_WindowsView) { CHAIN_MSG_MAP_ALT_MEMBER(m_WindowsView, 1) } END_MSG_MAP() LRESULT OnTreeNodeRightClick(HTREEITEM hItem, CPoint const& pt); LRESULT OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt); private: enum class ItemType { Process, Thread, Window }; void InitTree(); void UpdateUI(); CTreeItem AddNode(HWND hWnd, HTREEITEM hParent); void AddChildWindows(HTREEITEM hParent); BOOL AddChildNode(HWND hChild); void ChangeSelection(HTREEITEM hItem); LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnNodeExpanding(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnNodeDeleted(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnNodeSelected(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnWindowShow(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowHide(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowMinimize(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowMaximize(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowRestore(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleHiddenWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleEmptyTitleWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleChildWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowFlash(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowBringToFront(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnProcessProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); CWindowsListView m_WindowsView; CSplitterWindow m_Splitter; CTreeViewCtrlEx m_Tree; std::unordered_map m_Processes; std::unordered_map m_Threads; std::unordered_map m_WindowMap; CTreeItem m_hCurrentNode; CTreeItem m_Selected; CWindow m_SelectedHwnd; int m_TotalWindows, m_TotalVisibleWindows, m_TopLevelWindows; bool m_ShowHiddenWindows : 1 { true }; bool m_ShowNoTitleWindows : 1 { true }; bool m_ShowChildWindows : 1 { true }; }; ================================================ FILE: WinSpy/SecurityHelper.cpp ================================================ #include "pch.h" #include "SecurityHelper.h" bool SecurityHelper::IsRunningElevated() { static bool runningElevated = false; static bool runningElevatedCheck = false; if (runningElevatedCheck) return runningElevated; runningElevatedCheck = true; HANDLE hToken; if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) return false; TOKEN_ELEVATION te; DWORD len; if (::GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &len)) { runningElevated = te.TokenIsElevated ? true : false; } ::CloseHandle(hToken); return runningElevated; } bool SecurityHelper::RunElevated() { WCHAR path[MAX_PATH]; ::GetModuleFileName(nullptr, path, _countof(path)); return (INT_PTR)::ShellExecute(nullptr, L"runas", path, nullptr, nullptr, SW_SHOWDEFAULT) > 31; } bool SecurityHelper::EnablePrivilege(PCWSTR privName, bool enable) { HANDLE hToken; if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return false; bool result = false; TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; if (::LookupPrivilegeValue(nullptr, privName, &tp.Privileges[0].Luid)) { if (::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, nullptr)) result = ::GetLastError() == ERROR_SUCCESS; } ::CloseHandle(hToken); return result; } ================================================ FILE: WinSpy/SecurityHelper.h ================================================ #pragma once struct SecurityHelper abstract final { static bool IsRunningElevated(); static bool RunElevated(); static bool EnablePrivilege(PCWSTR privName, bool enable = true); }; ================================================ FILE: WinSpy/TreeViewManager.h ================================================ #pragma once template class CTreeViewManager { protected: BEGIN_MSG_MAP(CTreeViewManager) NOTIFY_CODE_HANDLER(NM_RCLICK, OnRightClick) NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDoubleClick) NOTIFY_CODE_HANDLER(TVN_SELCHANGED, OnTreeSelectionChanged) END_MSG_MAP() LRESULT OnTreeSelectionChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { return 0; } LRESULT OnRightClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { CTreeViewCtrl tv(pnmh->hwndFrom); CPoint pt; ::GetCursorPos(&pt); CPoint pt2(pt); tv.ScreenToClient(&pt2); auto hItem = tv.HitTest(pt2, nullptr); if (!hItem) return 0; tv.SelectItem(hItem); auto pT = static_cast(this); return pT->OnTreeNodeRightClick(hItem, pt); } LRESULT OnDoubleClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { CTreeViewCtrl tv(pnmh->hwndFrom); CPoint pt; ::GetCursorPos(&pt); CPoint pt2(pt); tv.ScreenToClient(&pt2); auto hItem = tv.HitTest(pt2, nullptr); if (!hItem) return 0; auto pT = static_cast(this); return pT->OnTreeNodeDoubleClick(hItem, pt); } private: // // overridables // LRESULT OnTreeNodeRightClick(HTREEITEM hItem, CPoint const& pt) { return 0; } LRESULT OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt) { return 0; } }; ================================================ FILE: WinSpy/ViewBase.h ================================================ #pragma once #include "Interfaces.h" template> class CViewBase abstract : public TBase, public CAutoUpdateUI, public CIdleHandler { public: CViewBase(IMainFrame* frame) : m_pFrame(frame) {} protected: BEGIN_MSG_MAP(CViewBase) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(TBVN_PAGEACTIVATED, OnTabActivated) CHAIN_MSG_MAP(TBase) END_MSG_MAP() IMainFrame* GetFrame() { return m_pFrame; } void OnFinalMessage(HWND) override { delete this; } BOOL OnIdle() override { CAutoUpdateUI::UIUpdateToolBar(); return FALSE; } LRESULT OnTabActivated(UINT /*uMsg*/, WPARAM wParam, LPARAM, BOOL& bHandled) { auto pT = static_cast(this); pT->OnActivate(wParam ? true : false); return 0; } void OnActivate(bool activate) {} LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { auto pT = static_cast(this); bHandled = FALSE; if (pT->m_hWndToolBar) _Module.GetMessageLoop()->RemoveIdleHandler(this); return 0; } HWND CreateAndInitToolBar(const ToolBarButtonInfo* buttons, int count) { auto pT = static_cast(this); CToolBarCtrl tb; auto hWndToolBar = tb.Create(pT->m_hWnd, pT->rcDefault, nullptr, ATL_SIMPLE_TOOLBAR_PANE_STYLE | TBSTYLE_LIST, 0, ATL_IDW_TOOLBAR); tb.SetExtendedStyle(TBSTYLE_EX_MIXEDBUTTONS); CImageList tbImages; tbImages.Create(24, 24, ILC_COLOR32 | ILC_COLOR | ILC_MASK, 4, 4); tb.SetImageList(tbImages); for (int i = 0; i < count; i++) { auto& b = buttons[i]; if (b.id == 0) tb.AddSeparator(0); else { int image = b.image == 0 ? I_IMAGENONE : tbImages.AddIcon(AtlLoadIconImage(b.image, 0, 24, 24)); tb.AddButton(b.id, b.style | (b.text ? BTNS_SHOWTEXT : 0), TBSTATE_ENABLED, image, b.text, 0); } } pT->CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE); pT->AddSimpleReBarBand(tb); UIAddToolBar(hWndToolBar); _Module.GetMessageLoop()->AddIdleHandler(this); return hWndToolBar; } IMainFrame* m_pFrame; }; ================================================ FILE: WinSpy/WinSpy.cpp ================================================ // WinSpy.cpp : main source file for WinSpy.exe // #include "pch.h" #include "resource.h" #include "MainFrm.h" #include CAppModule _Module; int Run(LPCTSTR /*lpstrCmdLine*/ = nullptr, int nCmdShow = SW_SHOWDEFAULT) { CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop); CMainFrame wndMain; if (wndMain.CreateEx() == nullptr) { ATLTRACE(_T("Main window creation failed!\n")); return 0; } wndMain.ShowWindow(nCmdShow); int nRet = theLoop.Run(); _Module.RemoveMessageLoop(); return nRet; } int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { HRESULT hRes = ::CoInitialize(nullptr); ATLASSERT(SUCCEEDED(hRes)); AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES); hRes = _Module.Init(nullptr, hInstance); ATLASSERT(SUCCEEDED(hRes)); ThemeHelper::Init(); int nRet = Run(lpstrCmdLine, nCmdShow); _Module.Term(); ::CoUninitialize(); return nRet; } ================================================ FILE: WinSpy/WinSpy.h ================================================ // WinSpy.h ================================================ FILE: WinSpy/WinSpy.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "atlres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""atlres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Menu // IDR_MAINFRAME MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Run As Administrator...", ID_FILE_RUNASADMINISTRATOR MENUITEM SEPARATOR MENUITEM "E&xit", ID_APP_EXIT END POPUP "&Edit" BEGIN MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE END POPUP "&View" BEGIN MENUITEM "&Refresh", ID_VIEW_REFRESH MENUITEM SEPARATOR MENUITEM "&Toolbar", ID_VIEW_TOOLBAR MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR MENUITEM SEPARATOR MENUITEM "&Windows Tree", ID_VIEW_ALLWINDOWS MENUITEM "&Processes Tree", ID_VIEW_ALLPROCESSES MENUITEM "Windows &List", ID_VIEW_WINDOWSLIST MENUITEM "&Automation Tree", ID_VIEW_AUTOMATIONTREE MENUITEM SEPARATOR MENUITEM "&Hidden Windows", ID_VIEW_HIDDENWINDOWS MENUITEM "EmptyTitle Windows", ID_VIEW_EMPTYTITLEWINDOWS END POPUP "&Window" BEGIN MENUITEM "&Find...\tCtrl+W", ID_WINDOW_FIND MENUITEM "&Messages...", ID_WINDOW_MESSAGES MENUITEM SEPARATOR MENUITEM "Locate in &Tree", ID_WINDOW_LOCATEINTREE POPUP "&Action" BEGIN MENUITEM "&Show", ID_WINDOW_SHOW MENUITEM "&Hide", ID_WINDOW_HIDE MENUITEM "&Highlight", ID_WINDOW_HIGHLIGHT MENUITEM "&Minimize", ID_WINDOW_MINIMIZE MENUITEM "Ma&ximize", ID_WINDOW_MAXIMIZE MENUITEM "&Restore", ID_WINDOW_RESTORE MENUITEM "&Bring To Front", ID_WINDOW_BRINGTOFRONT MENUITEM "&Close", ID_STATE_CLOSE MENUITEM "&Flash", ID_STATE_FLASH END MENUITEM SEPARATOR MENUITEM "&Properties...", ID_WINDOW_PROPERTIES END POPUP "&Options" BEGIN MENUITEM "&Always On Top", ID_OPTIONS_ALWAYSONTOP MENUITEM "&Fonts...", ID_OPTIONS_FONTS END POPUP "&Tab" BEGIN MENUITEM "&Close\tCtrl+F4", ID_WINDOW_CLOSE MENUITEM "Close &All", ID_WINDOW_CLOSE_ALL END POPUP "&Help" BEGIN MENUITEM "&About WinSpy...", ID_APP_ABOUT END END IDR_CONTEXT MENU BEGIN POPUP "tree" BEGIN MENUITEM "&Show", ID_WINDOW_SHOW MENUITEM "&Hide", ID_WINDOW_HIDE MENUITEM "&Highlight", ID_WINDOW_HIGHLIGHT MENUITEM "&Minimize", ID_WINDOW_MINIMIZE MENUITEM "Ma&ximize", ID_WINDOW_MAXIMIZE MENUITEM "&Restore", ID_WINDOW_RESTORE MENUITEM "&Bring To Front", ID_WINDOW_BRINGTOFRONT MENUITEM "Send to &Back", ID_TREE_SENDTOBACK MENUITEM "&Close", ID_STATE_CLOSE MENUITEM "&Flash", ID_STATE_FLASH MENUITEM "&Messages...", ID_WINDOW_MESSAGES MENUITEM SEPARATOR MENUITEM "&Properties...", ID_WINDOW_PROPERTIES END POPUP "list" BEGIN POPUP "&State" BEGIN MENUITEM "&Show", ID_WINDOW_SHOW MENUITEM "&Hide", ID_WINDOW_HIDE MENUITEM "&Highlight", ID_WINDOW_HIGHLIGHT MENUITEM "&Minimize", ID_WINDOW_MINIMIZE MENUITEM "Ma&ximize", ID_WINDOW_MAXIMIZE MENUITEM "&Restore", ID_WINDOW_RESTORE MENUITEM "&Bring To Front", ID_WINDOW_BRINGTOFRONT MENUITEM "&Close", ID_STATE_CLOSE MENUITEM "&Flash", ID_STATE_FLASH END MENUITEM SEPARATOR MENUITEM "&Properties...", ID_WINDOW_PROPERTIES MENUITEM SEPARATOR MENUITEM "Locate in &Tree", ID_WINDOW_LOCATEINTREE END POPUP "process" BEGIN MENUITEM "&Terminate", ID_PROCESS_TERMINATE MENUITEM SEPARATOR MENUITEM "&Properties...", ID_PROCESS_PROPERTIES END POPUP "thread" BEGIN MENUITEM "&Properties...", ID_THREAD_PROPERTIES MENUITEM SEPARATOR MENUITEM "&Messages...", ID_WINDOW_MESSAGES END END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDR_MAINFRAME ICON "res\\WinSpy.ico" IDI_WINDOW ICON "res\\window.ico" IDI_WINDOW_HIDDEN ICON "res\\window-hidden.ico" IDI_WINDOW_DELETE ICON "res\\window_delete.ico" IDI_WINDOW_NEW ICON "res\\window_new.ico" IDI_WINDOWS ICON "res\\windows.ico" IDI_REFRESH ICON "res\\refresh.ico" IDI_WINDOW_CLOSE ICON "res\\close-window.ico" IDI_WINDOW_MAXIMIZE ICON "res\\maximize.ico" IDI_WINDOW_NOTEXT ICON "res\\window-notext.ico" IDI_WINDOW_MINIMIZE ICON "res\\Minimize.ico" IDI_WINDOW_SENDTOBACK ICON "res\\WindowSendToBack.ico" IDI_TARGET ICON "res\\Target.ico" IDI_PROCESSES ICON "res\\gears.ico" IDI_WINPROP ICON "res\\window-properties.ico" IDI_PROCESS_INFO ICON "res\\gear_information.ico" IDI_MESSAGE ICON "res\\message.ico" IDI_MESSAGES ICON "res\\messages.ico" IDI_WINDOWSEARCH ICON "res\\WindowSearch.ico" IDI_SENDTOFRONT ICON "res\\WindowSendToFront.ico" IDI_RESTORE ICON "res\\restore.ico" IDI_AUTOMATION ICON "res\\Automation.ico" ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_ABOUTBOX DIALOGEX 0, 0, 187, 85 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About WinSpy" FONT 9, "Segoe UI", 0, 0, 0x0 BEGIN DEFPUSHBUTTON "OK",IDOK,70,64,48,14 CTEXT "WinSpy v0.3\n\n2020-2024 Pavel Yosifovich",IDC_STATIC,46,14,103,32 ICON IDR_MAINFRAME,IDC_STATIC,6,6,20,20 END IDD_FINDWINDOW DIALOGEX 0, 0, 333, 146 STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Find Window" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Handle:",IDC_STATIC,9,9,26,8 EDITTEXT IDC_HANDLE,41,7,69,14,ES_AUTOHSCROLL LTEXT "Class:",IDC_STATIC,9,30,20,8 EDITTEXT IDC_CLASSNAME,41,27,285,14,ES_AUTOHSCROLL LTEXT "Text:",IDC_STATIC,16,50,18,8 EDITTEXT IDC_TEXT,41,48,285,14,ES_AUTOHSCROLL LTEXT "Drag and drop over a window",IDC_STATIC,12,78,96,8 ICON IDI_TARGET,IDC_TARGET,119,72,20,20,SS_NOTIFY | SS_CENTERIMAGE PUSHBUTTON "Search",IDC_SEARCH,163,75,50,14 PUSHBUTTON "Cancel",IDCANCEL,174,125,50,14 PUSHBUTTON "OK",IDOK,108,125,50,14 CONTROL "Hide main window while dragging",IDC_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,95,123,10 LTEXT "Thread:",IDC_STATIC,116,9,26,8 EDITTEXT IDC_THREAD,145,7,40,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Process:",IDC_STATIC,195,9,28,8 EDITTEXT IDC_PROCESS,228,7,98,14,ES_AUTOHSCROLL | ES_READONLY END IDD_WINPROP DIALOGEX 0, 0, 433, 243 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD | WS_CAPTION | WS_SYSMENU CAPTION "General" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Handle:",IDC_STATIC,7,9,26,8 EDITTEXT IDC_HANDLE,35,7,69,14,ES_AUTOHSCROLL LTEXT "Class:",IDC_STATIC,10,30,20,8 EDITTEXT IDC_CLASSNAME,35,27,306,14,ES_AUTOHSCROLL LTEXT "Text:",IDC_STATIC,12,50,18,8 EDITTEXT IDC_TEXT,35,48,388,14,ES_AUTOHSCROLL LTEXT "Thread:",IDC_STATIC,116,9,26,8 EDITTEXT IDC_THREAD,145,7,43,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Process:",IDC_STATIC,203,9,28,8 EDITTEXT IDC_PROCESS,239,7,151,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Window Rect:",IDC_STATIC,7,71,46,8 EDITTEXT IDC_RECT,7,82,129,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Client Rect:",IDC_STATIC,155,71,38,8 EDITTEXT IDC_CLIENTRECT,153,81,129,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Style:",IDC_STATIC,16,129,20,8 EDITTEXT IDC_STYLE,42,126,61,14,ES_AUTOHSCROLL | ES_READONLY LISTBOX IDC_STYLES,7,145,138,94,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP LTEXT "Extended Style:",IDC_STATIC,166,129,52,8 EDITTEXT IDC_STYLEEX,222,126,61,14,ES_AUTOHSCROLL | ES_READONLY LISTBOX IDC_STYLESEX,150,145,160,93,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP LTEXT "Atom:",IDC_STATIC,361,31,20,8 EDITTEXT IDC_ATOM,385,28,42,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Restore Rect:",IDC_STATIC,298,71,46,8 EDITTEXT IDC_RESTORE_RECT,297,82,129,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Menu / ID:",IDC_STATIC,7,105,35,8 EDITTEXT IDC_MENU,45,102,69,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "User Data:",IDC_STATIC,135,106,36,8 EDITTEXT IDC_USERDATA,178,103,80,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Class Style:",IDC_STATIC,320,129,38,8 EDITTEXT IDC_STYLECLASS,364,127,61,14,ES_AUTOHSCROLL | ES_READONLY LISTBOX IDC_CLASSSTYLE,322,146,109,93,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP END IDD_PROPWINDOWS DIALOGEX 0, 0, 401, 247 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Windows" FONT 8, "MS Shell Dlg", 400, 0, 0x0 BEGIN GROUPBOX "Parent Window",IDC_STATIC,7,7,387,56 LTEXT "Handle:",IDC_STATIC,15,23,26,8 LTEXT "Class:",IDC_STATIC,118,22,20,8 EDITTEXT IDC_CLASS,148,20,246,14,ES_AUTOHSCROLL | ES_READONLY CONTROL "SysLink1",IDC_HANDLE,"SysLink",LWS_RIGHT | WS_TABSTOP,45,23,60,10 LTEXT "Text:",IDC_STATIC,15,43,18,8 EDITTEXT IDC_TEXT,39,41,355,14,ES_AUTOHSCROLL | ES_READONLY GROUPBOX "Next Window",IDC_STATIC,7,66,387,56 LTEXT "Handle:",IDC_STATIC,15,82,26,8 LTEXT "Class:",IDC_STATIC,118,81,20,8 EDITTEXT IDC_CLASS2,148,79,246,14,ES_AUTOHSCROLL | ES_READONLY CONTROL "SysLink1",IDC_HANDLE2,"SysLink",LWS_RIGHT | WS_TABSTOP,45,82,60,10 LTEXT "Text:",IDC_STATIC,15,102,18,8 EDITTEXT IDC_TEXT2,39,100,355,14,ES_AUTOHSCROLL | ES_READONLY GROUPBOX "Previous Window",IDC_STATIC,7,126,387,56 LTEXT "Handle:",IDC_STATIC,15,142,26,8 LTEXT "Class:",IDC_STATIC,118,142,20,8 EDITTEXT IDC_CLASS3,148,139,246,14,ES_AUTOHSCROLL | ES_READONLY CONTROL "SysLink1",IDC_HANDLE3,"SysLink",LWS_RIGHT | WS_TABSTOP,45,142,60,10 LTEXT "Text:",IDC_STATIC,15,162,18,8 EDITTEXT IDC_TEXT3,39,161,355,14,ES_AUTOHSCROLL | ES_READONLY GROUPBOX "First Child Window",IDC_STATIC,7,184,387,56 LTEXT "Handle:",IDC_STATIC,15,200,26,8 LTEXT "Class:",IDC_STATIC,118,200,20,8 EDITTEXT IDC_CLASS4,148,197,246,14,ES_AUTOHSCROLL | ES_READONLY CONTROL "SysLink1",IDC_HANDLE4,"SysLink",LWS_RIGHT | WS_TABSTOP,45,200,60,10 LTEXT "Text:",IDC_STATIC,15,220,18,8 EDITTEXT IDC_TEXT4,39,219,355,14,ES_AUTOHSCROLL | ES_READONLY END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_ABOUTBOX, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 180 TOPMARGIN, 7 BOTTOMMARGIN, 78 END IDD_FINDWINDOW, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 326 TOPMARGIN, 7 BOTTOMMARGIN, 139 END IDD_WINPROP, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 426 TOPMARGIN, 7 BOTTOMMARGIN, 236 END IDD_PROPWINDOWS, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 394 TOPMARGIN, 7 BOTTOMMARGIN, 240 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Accelerator // IDR_MAINFRAME ACCELERATORS BEGIN "N", ID_FILE_NEW, VIRTKEY, CONTROL "O", ID_FILE_OPEN, VIRTKEY, CONTROL "S", ID_FILE_SAVE, VIRTKEY, CONTROL "P", ID_FILE_PRINT, VIRTKEY, CONTROL "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL "X", ID_EDIT_CUT, VIRTKEY, CONTROL "C", ID_EDIT_COPY, VIRTKEY, CONTROL "V", ID_EDIT_PASTE, VIRTKEY, CONTROL VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT VK_F6, ID_NEXT_PANE, VIRTKEY VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT END ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 0,3,0,0 PRODUCTVERSION 0,3,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Scorpio Software" VALUE "FileDescription", "WinSpy - UI Windows Properties and Messages" VALUE "FileVersion", "0.3.0.0" VALUE "InternalName", "WinSpy" VALUE "LegalCopyright", "2021-2024 Pavel Yosifovich" VALUE "OriginalFilename", "WinSpy.exe" VALUE "ProductName", "WinSpy" VALUE "ProductVersion", "0.3.0.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END ///////////////////////////////////////////////////////////////////////////// // // AFX_DIALOG_LAYOUT // IDD_ABOUTBOX AFX_DIALOG_LAYOUT BEGIN 0 END IDD_FINDWINDOW AFX_DIALOG_LAYOUT BEGIN 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 100, 0, 0, 50, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0 END IDD_WINPROP AFX_DIALOG_LAYOUT BEGIN 0 END IDD_PROPWINDOWS AFX_DIALOG_LAYOUT BEGIN 0 END ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN IDR_MAINFRAME "WinSpy v0.3" IDS_TITLE "WinSpy" END STRINGTABLE BEGIN ID_FILE_NEW "Create a new document\nNew" ID_FILE_OPEN "Open an existing document\nOpen" ID_FILE_CLOSE "Close the active document\nClose" ID_FILE_SAVE "Save the active document\nSave" ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" ID_FILE_PRINT "Print the active document\nPrint" ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" END STRINGTABLE BEGIN ID_APP_ABOUT "Display program information, version number and copyright\nAbout" ID_APP_EXIT "Quit the application; prompts to save documents\nExit" END STRINGTABLE BEGIN ID_NEXT_PANE "Switch to the next window pane\nNext Pane" ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" END STRINGTABLE BEGIN ID_WINDOW_NEW "Open another window for the active document\nNew Window" ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" ID_WINDOW_SPLIT "Split the active window into panes\nSplit" END STRINGTABLE BEGIN ID_EDIT_CLEAR "Erase the selection\nErase" ID_EDIT_CLEAR_ALL "Erase everything\nErase All" ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" ID_EDIT_FIND "Find the specified text\nFind" ID_EDIT_PASTE "Insert Clipboard contents\nPaste" ID_EDIT_REPEAT "Repeat the last action\nRepeat" ID_EDIT_REPLACE "Replace specific text with different text\nReplace" ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" ID_EDIT_UNDO "Undo the last action\nUndo" ID_EDIT_REDO "Redo the previously undone action\nRedo" END STRINGTABLE BEGIN ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" ID_VIEW_REFRESH "\nRefresh" END STRINGTABLE BEGIN ATL_IDS_SCSIZE "Change the window size" ATL_IDS_SCMOVE "Change the window position" ATL_IDS_SCMINIMIZE "Reduce the window to an icon" ATL_IDS_SCMAXIMIZE "Enlarge the window to full size" ATL_IDS_SCNEXTWINDOW "Switch to the next document window" ATL_IDS_SCPREVWINDOW "Switch to the previous document window" ATL_IDS_SCCLOSE "Close the active window and prompts to save the documents" END STRINGTABLE BEGIN ATL_IDS_SCRESTORE "Restore the window to normal size" ATL_IDS_SCTASKLIST "Activate Task List" ATL_IDS_MDICHILD "Activate this window" END STRINGTABLE BEGIN ATL_IDS_IDLEMESSAGE "Ready" END STRINGTABLE BEGIN ATL_IDS_MRU_FILE "Open this document" END STRINGTABLE BEGIN ID_WINDOW_CLOSE "\nClose Tab" ID_WINDOW_CLOSE_ALL "\nClose All Tabs" ID_VIEW_ALLWINDOWS "\nWindow Tree" ID_VIEW_ALLPROCESSES "\nProcesses, Threads && Windows" ID_WINDOW_SHOW "\nShow Window" ID_WINDOW_HIDE "\nHide Window" ID_WINDOW_HIGHLIGHT "\nHighlight Window" END STRINGTABLE BEGIN ID_WINDOW_PROPERTIES "\nWindow Properties" ID_WINDOW_FIND "\nFind Window" ID_VIEW_HIDDENWINDOWS "\nHidden Windows" ID_VIEW_EMPTYTITLEWINDOWS "\nWindows with no Titles" END STRINGTABLE BEGIN ID_WINDOW_MESSAGES "\nMonitor Messages" END #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: WinSpy/WinSpy.vcxproj ================================================ Debug Win32 ReleaseSigned Win32 ReleaseSigned x64 Release Win32 Debug x64 Release x64 16.0 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7} 10.0 Application true v143 Unicode Application false v143 Unicode Application false v143 Unicode Application true v143 Unicode Application false v143 Unicode Application false v143 Unicode true ..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath); true ..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath); false ..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath); false ..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath); false ..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath); false ..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath); Use Level3 MultiThreadedDebug EditAndContinue EnableFastChecks Disabled WIN32;_WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions) pch.h false stdcpp20 ..\WinSpyHook;..\WTLHelper\WTLHelper Windows true 0x0409 $(IntDir);%(AdditionalIncludeDirectories) _DEBUG;%(PreprocessorDefinitions) false Win32 _DEBUG;%(PreprocessorDefinitions) WinSpy.h WinSpy_i.c WinSpy_p.c true $(IntDir)/WinSpy.tlb false Use Level3 MultiThreadedDebug EditAndContinue EnableFastChecks Disabled _WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions) pch.h false stdcpp20 ..\WinSpyHook;..\WTLHelper\WTLHelper Windows true 0x0409 $(IntDir);%(AdditionalIncludeDirectories) _DEBUG;%(PreprocessorDefinitions) false _DEBUG;%(PreprocessorDefinitions) WinSpy.h WinSpy_i.c WinSpy_p.c true $(IntDir)/WinSpy.tlb false Use Level3 MultiThreaded false WIN32;_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions) pch.h stdcpp20 ..\WinSpyHook;..\WTLHelper\WTLHelper Windows 0x0409 $(IntDir);%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) false Win32 NDEBUG;%(PreprocessorDefinitions) WinSpy.h WinSpy_i.c WinSpy_p.c true $(IntDir)/WinSpy.tlb false Use Level3 MultiThreaded false WIN32;_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions) pch.h stdcpp20 ..\WinSpyHook;..\WTLHelper\WTLHelper Windows 0x0409 $(IntDir);%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) false Win32 NDEBUG;%(PreprocessorDefinitions) WinSpy.h WinSpy_i.c WinSpy_p.c true $(IntDir)/WinSpy.tlb signtool sign /i DigiCert /fd sha256 $(TargetPath) false Use Level3 MultiThreaded false _WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions) pch.h stdcpp20 ..\WinSpyHook;..\WTLHelper\WTLHelper Windows 0x0409 $(IntDir);%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) false NDEBUG;%(PreprocessorDefinitions) WinSpy.h WinSpy_i.c WinSpy_p.c true $(IntDir)/WinSpy.tlb false Use Level3 MultiThreaded false _WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions) pch.h stdcpp20 ..\WinSpyHook;..\WTLHelper\WTLHelper Windows 0x0409 $(IntDir);%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) false NDEBUG;%(PreprocessorDefinitions) WinSpy.h WinSpy_i.c WinSpy_p.c true $(IntDir)/WinSpy.tlb signtool sign /a /n Scorpio /t http://timestamp.digicert.com /fd sha256 $(TargetPath) false Create Create Create Create Create Create {ae53419f-a769-4548-8e15-e311904df7df} ================================================ FILE: WinSpy/WinSpy.vcxproj.filters ================================================  {f871bb5c-f151-4e10-8bbb-10019ddd6a72} cpp;c;cxx;def;odl;idl;hpj;bat;asm {47fb17d7-d54c-4fc7-85b2-7c653d04b30a} h;hpp;hxx;hm;inl;inc {9c7daf0d-2916-49f7-872d-30cf5e95725d} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest {ff137a25-faa3-4d88-af38-5c1bc296546b} {96be1941-f83d-4366-ad55-930da999a2eb} {9a542756-dfdb-4a47-b231-2b2ae2aef120} {3b473b33-73a4-4bd6-a469-d86318a53f81} Source Files Source Files Source Files Source Files Dialogs Views Helpers Helpers Helpers Views Helpers Views Helpers Dialogs Dialogs Helpers Views Helpers Helpers Views Header Files Header Files Header Files Header Files Header Files Header Files Dialogs Views Views Helpers Helpers Resource Files Helpers Views Helpers Helpers Dialogs Helpers Dialogs Helpers Views Helpers Helpers Helpers Views Resource Files Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons Resource Files\Icons ================================================ FILE: WinSpy/WindowGeneralPage.cpp ================================================ #include "pch.h" #include "resource.h" #include "WindowGeneralPage.h" #include "FormatHelper.h" #include "ProcessHelper.h" #include "WindowHelper.h" void CWindowGeneralPage::UpdateData() { WINDOWINFO wi{ sizeof(wi) }; ::GetWindowInfo(m_Win, &wi); GetParent().SetIcon(WindowHelper::GetWindowIcon(m_Win)); SetDlgItemText(IDC_RECT, FormatHelper::RectToString(wi.rcWindow)); SetDlgItemText(IDC_CLIENTRECT, FormatHelper::RectToString(wi.rcClient)); DWORD pid; auto tid = ::GetWindowThreadProcessId(m_Win, &pid); SetDlgItemInt(IDC_THREAD, tid, FALSE); CString text; text.Format(L"0x%zX", DWORD_PTR(m_Win.m_hWnd)); SetDlgItemText(IDC_HANDLE, text); if (tid) { text.Format(L"%s (%u)", (PCWSTR)ProcessHelper::GetProcessImageName(pid), pid); SetDlgItemText(IDC_PROCESS, text); } WINDOWPLACEMENT wp{ sizeof(wp) }; m_Win.GetWindowPlacement(&wp); SetDlgItemText(IDC_RESTORE_RECT, FormatHelper::RectToString(wp.rcNormalPosition)); m_Win.GetWindowText(text); SetDlgItemText(IDC_TEXT, text); text.Format(L"0x%04X\n", wi.atomWindowType); SetDlgItemText(IDC_ATOM, text); text.Format(L"0x%08X\n", wi.dwStyle); SetDlgItemText(IDC_STYLE, text); text.Format(L"0x%08X\n", wi.dwExStyle); SetDlgItemText(IDC_STYLEEX, text); text.Format(L"0x%zX", m_Win.GetMenu() ? (ULONG_PTR)m_Win.GetMenu() : WindowHelper::GetID(m_Win)); SetDlgItemText(IDC_MENU, text); text.Format(L"0x%zX", m_Win.GetWindowLongPtr(GWLP_USERDATA)); SetDlgItemText(IDC_USERDATA, text); WCHAR className[128]; if (::GetClassName(m_Win, className, _countof(className))) SetDlgItemText(IDC_CLASSNAME, className); auto clsStyle = (DWORD)::GetClassLongPtr(m_Win, GCL_STYLE); text.Format(L"0x%08X", clsStyle); SetDlgItemText(IDC_STYLECLASS, text); FillStyleList(wi.dwStyle, WindowHelper::GetWindowStyleArray(), IDC_STYLES, L"", L"WS_OVERLAPPED"); FillSpecificControlStyles(); FillStyleList(wi.dwExStyle, WindowHelper::GetWindowStyleExArray(), IDC_STYLESEX, L"", L"WS_EX_LEFT"); FillStyleList(clsStyle, WindowHelper::GetClassStyleArray(), IDC_CLASSSTYLE, L"", nullptr); } void CWindowGeneralPage::FillStyleList(DWORD style, std::pair styles, UINT id, PCWSTR prefix, PCWSTR defaultStyle, bool clear) { auto const [items, count] = styles; CListBox lb(GetDlgItem(id)); ATLASSERT(lb); if(clear) lb.ResetContent(); CString text; for (int i = 0; i < count; i++) { if ((style & items[i].Mask) == items[i].Value) { text.Format(L"%s%s (0x%X)", prefix, (PCWSTR)items[i].Text, items[i].Value); lb.AddString(text); } } if (style == 0 && defaultStyle) lb.AddString(CString(defaultStyle) + L" (0)"); } void CWindowGeneralPage::FillSpecificControlStyles() { WCHAR name[64]; if (!::GetClassName(m_Win, name, _countof(name))) return; auto style = m_Win.GetStyle() & 0xffff; const CString ATLPrefix = L"ATL:"; static const struct { PCWSTR className; std::pair items; PCWSTR def = L""; } controls[] = { { WC_LISTVIEW, WindowHelper::GetListViewStyleArray(), L"LVS_ALIGNTOP" }, { WC_TREEVIEW, WindowHelper::GetTreeViewStyleArray() }, { WC_TABCONTROL, WindowHelper::GetTabCtrlStyleArray(), L"TCS_TABS" }, { WC_LISTBOX, WindowHelper::GetListBoxStyleArray() }, { WC_COMBOBOX, WindowHelper::GetComboBoxStyleArray() }, { WC_EDIT, WindowHelper::GetEditStyleArray(), L"ES_LEFT" }, { WC_BUTTON, WindowHelper::GetButtonStyleArray(), L"BS_TEXT" }, { WC_HEADER, WindowHelper::GetHeaderStyleArray(), L"HDS_HORZ" }, { WC_STATIC, WindowHelper::GetStaticStyleArray(), L"SS_LEFT" }, { TOOLTIPS_CLASS, WindowHelper::GetToolTipStyleArray() }, { STATUSCLASSNAME, WindowHelper::GetStatusBarStyleArray() }, { TOOLBARCLASSNAME, WindowHelper::GetToolBarStyleArray() }, { REBARCLASSNAME, WindowHelper::GetRebarStyleArray() }, { WC_SCROLLBAR, WindowHelper::GetScrollBarStyleArray(), L"SBS_HORZ" }, }; for (auto& item : controls) { if (_wcsicmp(name, item.className) == 0 || _wcsicmp(name, ATLPrefix + item.className) == 0) { FillStyleList(style, item.items, IDC_STYLES, L"", item.def, false); break; } } } LRESULT CWindowGeneralPage::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) { UpdateData(); return 0; } LRESULT CWindowGeneralPage::OnUpdate(UINT, WPARAM, LPARAM lp, BOOL&) { ATLASSERT(lp); m_Win.Detach(); m_Win.Attach(reinterpret_cast(lp)); UpdateData(); return 0; } ================================================ FILE: WinSpy/WindowGeneralPage.h ================================================ #pragma once #include "DialogHelper.h" struct StyleItem; class CWindowGeneralPage : public CDialogHelper, public CPropertyPageImpl { public: enum { IDD = IDD_WINPROP }; const UINT WM_UPDATE = WM_USER + 300; CWindowGeneralPage(HWND hWnd) : m_Win(hWnd) { m_psp.dwFlags |= PSP_USEICONID; m_psp.pszIcon = MAKEINTRESOURCE(IDI_WINPROP); } void UpdateData(); protected: BEGIN_MSG_MAP(CWindowGeneralPage) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) MESSAGE_HANDLER(WM_UPDATE, OnUpdate) END_MSG_MAP() private: void FillStyleList(DWORD style, std::pair styles, UINT id, PCWSTR prefix, PCWSTR defaultStyle, bool clear = true); void FillSpecificControlStyles(); // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnUpdate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); CWindow m_Win; }; ================================================ FILE: WinSpy/WindowHelper.cpp ================================================ #include "pch.h" #include "resource.h" #include "WindowHelper.h" #include "ProcessHelper.h" #include "FormatHelper.h" #include "WindowGeneralPage.h" #include "WindowWindowsPage.h" #define CASE_STR(x) case x: return L#x #define PAIR_STR(x) { x, L#x } #define PAIR_STR2(x, mask) { x, L#x, mask } CString WindowHelper::WindowStyleToString(HWND hWnd) { auto style = CWindow(hWnd).GetStyle(); CString text; auto const [styles, count] = GetWindowStyleArray(); for (int i = 0; i < count; i++) { auto& item = styles[i]; if ((style & item.Value) == item.Value) text += CString(item.Text) += L", "; } if (text.IsEmpty()) text = L"OVERLAPPED, "; return L"(WS_) " + text.Left(text.GetLength() - 2); } CString WindowHelper::ClassStyleToString(HWND hWnd) { auto style = ::GetClassLongPtr(hWnd, GCL_STYLE); CString text; auto const [styles, count] = GetWindowStyleArray(); for (int i = 0; i < count; i++) { auto& item = styles[i]; if ((style & item.Value) == item.Value) text += CString(item.Text) += L", "; } if (text.IsEmpty()) return L""; return L"(CS_) " + text.Left(text.GetLength() - 2); } CString WindowHelper::WindowExtendedStyleToString(HWND hWnd) { auto style = CWindow(hWnd).GetExStyle(); CString text; auto const [styles, count] = GetWindowStyleArray(); for (int i = 0; i < count; i++) { auto& item = styles[i]; if ((style & item.Value) == item.Value) text += CString(item.Text) += L", "; } if (text.IsEmpty()) text = L"LEFT, RIGHTSCROLLBAR, "; return L"(WS_EX_) " + text.Left(text.GetLength() - 2); } CString WindowHelper::WindowRectToString(HWND hWnd) { CWindow win(hWnd); CRect rc; win.GetWindowRect(&rc); return FormatHelper::RectToString(rc); } CString WindowHelper::GetWindowClassName(HWND hWnd) { WCHAR name[128]; ::GetClassName(hWnd, name, _countof(name)); return name; } CString WindowHelper::GetWindowText(HWND hWnd) { CString text; CWindow(hWnd).GetWindowText(text); return text; } HICON WindowHelper::GetWindowOrProcessIcon(HWND hWnd) { auto hIcon = GetWindowIcon(hWnd); if (!hIcon) { DWORD pid = 0; ::GetWindowThreadProcessId(hWnd, &pid); if (pid) { ::ExtractIconEx(ProcessHelper::GetProcessImageName(pid, true), 0, nullptr, &hIcon, 1); } } return hIcon; } bool WindowHelper::Flash(HWND hWnd) { FLASHWINFO info = { sizeof(info) }; info.dwFlags = FLASHW_CAPTION; info.uCount = 3; info.hwnd = hWnd; return ::FlashWindowEx(&info); } void WindowHelper::HighlightBorder(HWND hWnd, bool highlight) { CRect rc; ::GetWindowRect(hWnd, &rc); rc.OffsetRect(-rc.left, -rc.top); CRgn rgn1; rc.InflateRect(2, 2); rgn1.CreateRectRgnIndirect(&rc); rc.DeflateRect(5, 5); CRgn rgn2; rgn2.CreateRectRgnIndirect(&rc); CRgn rgn; rgn.CreateRectRgn(0, 0, 1, 1); rgn.CombineRgn(rgn1, rgn2, RGN_DIFF); if (!highlight) { ::RedrawWindow(hWnd, nullptr, rgn, RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME); return; } CWindowDC dc(hWnd); CBrush b; b.CreateSolidBrush(RGB(255, 0, 0)); dc.FillRgn(rgn, b); } HICON WindowHelper::GetWindowIcon(HWND hWnd) { HICON hIcon{ nullptr }; ::SendMessageTimeout(hWnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT, 100, (DWORD_PTR*)&hIcon); if (!hIcon) { hIcon = (HICON)::GetClassLongPtr(hWnd, GCLP_HICONSM); } return hIcon; } CString WindowHelper::GetWindowClassAndTitle(HWND hWnd) { if (hWnd == nullptr || !::IsWindow(hWnd)) return L""; WCHAR className[64]; CString text; if (::GetClassName(hWnd, className, _countof(className))) text.Format(L"[%s]", className); CString title; CWindow(hWnd).GetWindowText(title); if (!title.IsEmpty()) text.Format(L"%s (%s)", (PCWSTR)text, (PCWSTR)title); return text; } std::unordered_map& WindowHelper::GetIconMap() { static std::unordered_map s_IconMap; return s_IconMap; } CImageList& WindowHelper::GetImageList() { static CImageList images; if (!images) { images.Create(16, 16, ILC_COLOR32, 64, 16); images.AddIcon(AtlLoadIconImage(IDI_WINDOW, 0, 16, 16)); } return images; } WindowItem WindowHelper::GetWindowInfo(HWND hWnd) { WindowItem wi; wi.hWnd = hWnd; wi.ThreadId = ::GetWindowThreadProcessId(hWnd, &wi.ProcessId); wi.ProcessName = ProcessHelper::GetProcessImageName(wi.ProcessId); return wi; } bool WindowHelper::ThreadHasWindows(DWORD tid) { bool hasWindows = false; ::EnumThreadWindows(tid, [](auto, auto param) { *reinterpret_cast(param) = true; return FALSE; }, reinterpret_cast(&hasWindows)); return hasWindows; } int WindowHelper::ShowWindowProperties(HWND hWnd) { CString text; text.Format(L"Window 0x%X Properties", PtrToUlong(hWnd)); CPropertySheet sheet((PCWSTR)text); sheet.m_psh.dwFlags |= PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP; CWindowGeneralPage general(hWnd); CWindowWindowsPage windows(hWnd); sheet.AddPage(general); sheet.AddPage(windows); sheet.DoModal(); return 0; } std::pair WindowHelper::GetWindowStyleArray() { static const StyleItem styles[] = { PAIR_STR(WS_POPUP), PAIR_STR(WS_CHILD), PAIR_STR(WS_MINIMIZE), PAIR_STR(WS_VISIBLE), PAIR_STR(WS_DISABLED), PAIR_STR(WS_CLIPSIBLINGS), PAIR_STR(WS_CLIPCHILDREN), PAIR_STR(WS_MAXIMIZE), PAIR_STR(WS_BORDER), PAIR_STR(WS_DLGFRAME), PAIR_STR(WS_VSCROLL), PAIR_STR(WS_HSCROLL), PAIR_STR(WS_SYSMENU), PAIR_STR(WS_THICKFRAME), PAIR_STR(WS_MINIMIZEBOX), PAIR_STR(WS_MAXIMIZEBOX), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetListViewStyleArray() { static const StyleItem styles[] = { PAIR_STR(LVS_ALIGNLEFT), PAIR_STR(LVS_AUTOARRANGE), PAIR_STR(LVS_EDITLABELS), PAIR_STR(LVS_ICON), PAIR_STR(LVS_LIST), PAIR_STR(LVS_NOCOLUMNHEADER), PAIR_STR(LVS_NOLABELWRAP), PAIR_STR(LVS_SINGLESEL), PAIR_STR(LVS_REPORT), PAIR_STR(LVS_SMALLICON), PAIR_STR(LVS_SORTASCENDING), PAIR_STR(LVS_SORTDESCENDING), PAIR_STR(LVS_SHOWSELALWAYS), PAIR_STR(LVS_SHAREIMAGELISTS), PAIR_STR(LVS_OWNERDATA), PAIR_STR(LVS_OWNERDRAWFIXED), PAIR_STR(LVS_NOSCROLL), PAIR_STR(LVS_NOSORTHEADER), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetTreeViewStyleArray() { static const StyleItem styles[] = { PAIR_STR(TVS_CHECKBOXES), PAIR_STR(TVS_DISABLEDRAGDROP), PAIR_STR(TVS_EDITLABELS), PAIR_STR(TVS_FULLROWSELECT), PAIR_STR(TVS_HASBUTTONS), PAIR_STR(TVS_HASLINES), PAIR_STR(TVS_LINESATROOT), PAIR_STR(TVS_NOHSCROLL), PAIR_STR(TVS_INFOTIP), PAIR_STR(TVS_NONEVENHEIGHT), PAIR_STR(TVS_NOTOOLTIPS), PAIR_STR(TVS_RTLREADING), PAIR_STR(TVS_SHOWSELALWAYS), PAIR_STR(TVS_SINGLEEXPAND), PAIR_STR(TVS_TRACKSELECT), PAIR_STR(TVS_NOSCROLL), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetTabCtrlStyleArray() { static const StyleItem styles[] = { PAIR_STR(TCS_BOTTOM), PAIR_STR(TCS_BUTTONS), PAIR_STR(TCS_FIXEDWIDTH), PAIR_STR(TCS_FLATBUTTONS), PAIR_STR(TCS_FOCUSNEVER), PAIR_STR(TCS_FOCUSONBUTTONDOWN), PAIR_STR(TCS_FORCEICONLEFT), PAIR_STR(TCS_FORCELABELLEFT), PAIR_STR(TCS_HOTTRACK), PAIR_STR(TCS_MULTILINE), PAIR_STR(TCS_MULTISELECT), PAIR_STR(TCS_OWNERDRAWFIXED), PAIR_STR(TCS_RAGGEDRIGHT), PAIR_STR(TCS_RIGHT), PAIR_STR(TCS_RIGHTJUSTIFY), PAIR_STR(TCS_SCROLLOPPOSITE), PAIR_STR(TCS_SINGLELINE), PAIR_STR(TCS_TOOLTIPS), PAIR_STR(TCS_VERTICAL), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetWindowStyleExArray() { static const StyleItem styles[] = { PAIR_STR(WS_EX_DLGMODALFRAME), PAIR_STR(WS_EX_NOPARENTNOTIFY), PAIR_STR(WS_EX_TOPMOST), PAIR_STR(WS_EX_ACCEPTFILES), PAIR_STR(WS_EX_TRANSPARENT), PAIR_STR(WS_EX_TOOLWINDOW), PAIR_STR(WS_EX_WINDOWEDGE), PAIR_STR(WS_EX_CLIENTEDGE), PAIR_STR(WS_EX_CONTEXTHELP), PAIR_STR(WS_EX_RIGHT), PAIR_STR(WS_EX_RTLREADING), PAIR_STR(WS_EX_LEFTSCROLLBAR), PAIR_STR(WS_EX_CONTROLPARENT), PAIR_STR(WS_EX_STATICEDGE), PAIR_STR(WS_EX_APPWINDOW), PAIR_STR(WS_EX_LAYERED), PAIR_STR(WS_EX_NOINHERITLAYOUT), PAIR_STR(WS_EX_NOREDIRECTIONBITMAP), PAIR_STR(WS_EX_LAYOUTRTL), PAIR_STR(WS_EX_COMPOSITED), PAIR_STR(WS_EX_NOACTIVATE), { 0x8000, L"WS_EX_FEEDBACK" }, { 0x80000000, L"WS_EX_ANSICREATOR" }, { 0x800000, L"WS_EX_NOPADDEDBORDER" }, { 0x2, L"WS_EX_DRAGOBJECT" }, }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetClassStyleArray() { static const StyleItem styles[] = { PAIR_STR(CS_HREDRAW), PAIR_STR(CS_VREDRAW), PAIR_STR(CS_DBLCLKS), PAIR_STR(CS_OWNDC), PAIR_STR(CS_CLASSDC), PAIR_STR(CS_PARENTDC), PAIR_STR(CS_SAVEBITS), PAIR_STR(CS_GLOBALCLASS), PAIR_STR(CS_BYTEALIGNCLIENT), PAIR_STR(CS_BYTEALIGNWINDOW), PAIR_STR(CS_IME), PAIR_STR(CS_NOCLOSE), PAIR_STR(CS_DROPSHADOW), { 0x8000, L"CS_SYSTEM" }, }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetEditStyleArray() { static const StyleItem styles[] = { PAIR_STR(ES_AUTOHSCROLL), PAIR_STR(ES_AUTOVSCROLL), PAIR_STR(ES_READONLY), PAIR_STR(ES_CENTER), PAIR_STR(ES_RIGHT), PAIR_STR(ES_NOHIDESEL), PAIR_STR(ES_LOWERCASE), PAIR_STR(ES_UPPERCASE), PAIR_STR(ES_WANTRETURN), PAIR_STR(ES_MULTILINE), PAIR_STR(ES_PASSWORD), PAIR_STR(ES_NUMBER), PAIR_STR(ES_OEMCONVERT), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetToolTipStyleArray() { static const StyleItem styles[] = { PAIR_STR(TTS_ALWAYSTIP), PAIR_STR(TTS_BALLOON), PAIR_STR(TTS_CLOSE), PAIR_STR(TTS_NOANIMATE), PAIR_STR(TTS_NOFADE), PAIR_STR(TTS_NOPREFIX), PAIR_STR(TTS_USEVISUALSTYLE), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetScrollBarStyleArray() { static const StyleItem styles[] = { PAIR_STR(SBS_VERT), PAIR_STR(SBS_SIZEBOX), PAIR_STR(SBS_SIZEGRIP), PAIR_STR(SBS_RIGHTALIGN), PAIR_STR(SBS_BOTTOMALIGN), PAIR_STR(SBS_TOPALIGN), PAIR_STR(SBS_LEFTALIGN), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetStatusBarStyleArray() { static const StyleItem styles[] = { PAIR_STR(SBARS_SIZEGRIP), PAIR_STR(SBARS_TOOLTIPS), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetToolBarStyleArray() { static const StyleItem styles[] = { PAIR_STR(TBSTYLE_ALTDRAG), PAIR_STR(TBSTYLE_FLAT), PAIR_STR(TBSTYLE_LIST), PAIR_STR(TBSTYLE_WRAPABLE), PAIR_STR(TBSTYLE_CUSTOMERASE), PAIR_STR(TBSTYLE_REGISTERDROP), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetRebarStyleArray() { static const StyleItem styles[] = { PAIR_STR(RBS_AUTOSIZE), PAIR_STR(RBS_BANDBORDERS), PAIR_STR(RBS_DBLCLKTOGGLE), PAIR_STR(RBS_FIXEDORDER), PAIR_STR(RBS_VERTICALGRIPPER), PAIR_STR(RBS_REGISTERDROP), PAIR_STR(RBS_VARHEIGHT), PAIR_STR(RBS_TOOLTIPS), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetStaticStyleArray() { static const StyleItem styles[] = { PAIR_STR2(SS_RIGHT, SS_TYPEMASK), PAIR_STR2(SS_BLACKFRAME, SS_TYPEMASK), PAIR_STR2(SS_GRAYFRAME, SS_TYPEMASK), PAIR_STR2(SS_SIMPLE, SS_TYPEMASK), PAIR_STR2(SS_GRAYRECT, SS_TYPEMASK), PAIR_STR2(SS_BLACKRECT, SS_TYPEMASK), PAIR_STR2(SS_ICON, SS_TYPEMASK), PAIR_STR2(SS_BITMAP, SS_TYPEMASK), PAIR_STR2(SS_OWNERDRAW, SS_TYPEMASK), PAIR_STR2(SS_ENHMETAFILE, SS_TYPEMASK), PAIR_STR2(SS_ETCHEDFRAME, SS_TYPEMASK), PAIR_STR2(SS_ETCHEDHORZ, SS_TYPEMASK), PAIR_STR2(SS_ETCHEDVERT, SS_TYPEMASK), PAIR_STR2(SS_WHITEFRAME, SS_TYPEMASK), PAIR_STR2(SS_LEFTNOWORDWRAP, SS_TYPEMASK), PAIR_STR2(SS_CENTER, SS_TYPEMASK), PAIR_STR(SS_NOPREFIX), PAIR_STR(SS_NOTIFY), PAIR_STR(SS_CENTERIMAGE), PAIR_STR(SS_SUNKEN), PAIR_STR(SS_EDITCONTROL), PAIR_STR(SS_RIGHTJUST), PAIR_STR(SS_REALSIZEIMAGE), PAIR_STR2(SS_PATHELLIPSIS, SS_ELLIPSISMASK), PAIR_STR2(SS_WORDELLIPSIS, SS_ELLIPSISMASK), PAIR_STR2(SS_ENDELLIPSIS, SS_ELLIPSISMASK), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetHeaderStyleArray() { static const StyleItem styles[] = { PAIR_STR(HDS_BUTTONS), PAIR_STR(HDS_CHECKBOXES), PAIR_STR(HDS_DRAGDROP), PAIR_STR(HDS_FILTERBAR), PAIR_STR(HDS_FLAT), PAIR_STR(HDS_FULLDRAG), PAIR_STR(HDS_HIDDEN), PAIR_STR(HDS_HOTTRACK), PAIR_STR(HDS_NOSIZING), PAIR_STR(HDS_OVERFLOW), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetButtonStyleArray() { static const StyleItem styles[] = { PAIR_STR2(BS_AUTO3STATE, BS_TYPEMASK), PAIR_STR2(BS_AUTOCHECKBOX, BS_TYPEMASK), PAIR_STR2(BS_AUTORADIOBUTTON, BS_TYPEMASK), PAIR_STR2(BS_GROUPBOX, BS_TYPEMASK), PAIR_STR2(BS_OWNERDRAW, BS_TYPEMASK), PAIR_STR2(BS_PUSHBOX, BS_TYPEMASK), PAIR_STR2(BS_CHECKBOX, BS_TYPEMASK), PAIR_STR2(BS_DEFPUSHBUTTON, BS_TYPEMASK), PAIR_STR2(BS_DEFSPLITBUTTON, BS_TYPEMASK), PAIR_STR(BS_CENTER), PAIR_STR(BS_RIGHT), PAIR_STR(BS_BITMAP), PAIR_STR(BS_BOTTOM), PAIR_STR(BS_COMMANDLINK), PAIR_STR(BS_FLAT), PAIR_STR(BS_NOTIFY), PAIR_STR(BS_MULTILINE), PAIR_STR(BS_TOP), PAIR_STR(BS_FLAT), PAIR_STR(BS_LEFTTEXT), PAIR_STR2(BS_USERBUTTON, BS_TYPEMASK), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetListBoxStyleArray() { static const StyleItem styles[] = { PAIR_STR(LBS_COMBOBOX), PAIR_STR(LBS_NOSEL), PAIR_STR(LBS_SORT), PAIR_STR(LBS_NOTIFY), PAIR_STR(LBS_NOREDRAW), PAIR_STR(LBS_MULTICOLUMN), PAIR_STR(LBS_MULTIPLESEL), PAIR_STR(LBS_HASSTRINGS), PAIR_STR(LBS_USETABSTOPS), PAIR_STR(LBS_NOINTEGRALHEIGHT), PAIR_STR(LBS_OWNERDRAWFIXED), PAIR_STR(LBS_OWNERDRAWVARIABLE), PAIR_STR(LBS_WANTKEYBOARDINPUT), PAIR_STR(LBS_EXTENDEDSEL), PAIR_STR(LBS_DISABLENOSCROLL), }; return std::make_pair(styles, (int)_countof(styles)); } std::pair WindowHelper::GetComboBoxStyleArray() { static const StyleItem styles[] = { PAIR_STR(CBS_AUTOHSCROLL), PAIR_STR(CBS_SIMPLE), PAIR_STR(CBS_SORT), PAIR_STR(CBS_DROPDOWN), PAIR_STR(CBS_DROPDOWNLIST), PAIR_STR(CBS_HASSTRINGS), PAIR_STR(CBS_HASSTRINGS), PAIR_STR(CBS_UPPERCASE), PAIR_STR(CBS_LOWERCASE), PAIR_STR(CBS_OWNERDRAWFIXED), PAIR_STR(CBS_OWNERDRAWVARIABLE), }; return std::make_pair(styles, (int)_countof(styles)); } CString WindowHelper::WindowMessageToString(DWORD msg) { switch (msg) { CASE_STR(WM_NULL); CASE_STR(WM_CREATE); CASE_STR(WM_DESTROY); CASE_STR(WM_MOVE); CASE_STR(WM_SIZE); CASE_STR(WM_ACTIVATE); CASE_STR(WM_SETFOCUS); CASE_STR(WM_KILLFOCUS); CASE_STR(WM_ENABLE); CASE_STR(WM_SETREDRAW); CASE_STR(WM_SETTEXT); CASE_STR(WM_GETTEXT); CASE_STR(WM_GETTEXTLENGTH); CASE_STR(WM_PAINT); CASE_STR(WM_CLOSE); CASE_STR(WM_QUERYENDSESSION); CASE_STR(WM_QUIT); CASE_STR(WM_QUERYOPEN); CASE_STR(WM_ERASEBKGND); CASE_STR(WM_SYSCOLORCHANGE); CASE_STR(WM_ENDSESSION); CASE_STR(WM_SHOWWINDOW); CASE_STR(WM_SETTINGCHANGE); CASE_STR(WM_DEVMODECHANGE); CASE_STR(WM_ACTIVATEAPP); CASE_STR(WM_FONTCHANGE); CASE_STR(WM_TIMECHANGE); CASE_STR(WM_CANCELMODE); CASE_STR(WM_SETCURSOR); CASE_STR(WM_MOUSEACTIVATE); CASE_STR(WM_CHILDACTIVATE); CASE_STR(WM_QUEUESYNC); CASE_STR(WM_GETMINMAXINFO); CASE_STR(WM_PAINTICON); CASE_STR(WM_ICONERASEBKGND); CASE_STR(WM_NEXTDLGCTL); CASE_STR(WM_SPOOLERSTATUS); CASE_STR(WM_DRAWITEM); CASE_STR(WM_MEASUREITEM); CASE_STR(WM_DELETEITEM); CASE_STR(WM_VKEYTOITEM); CASE_STR(WM_CHARTOITEM); CASE_STR(WM_SETFONT); CASE_STR(WM_GETFONT); CASE_STR(WM_SETHOTKEY); CASE_STR(WM_GETHOTKEY); CASE_STR(WM_QUERYDRAGICON); CASE_STR(WM_COMPAREITEM); CASE_STR(WM_GETOBJECT); CASE_STR(WM_COMPACTING); CASE_STR(WM_COMMNOTIFY); CASE_STR(WM_WINDOWPOSCHANGING); CASE_STR(WM_WINDOWPOSCHANGED); CASE_STR(WM_POWER); CASE_STR(WM_COPYDATA); CASE_STR(WM_CANCELJOURNAL); CASE_STR(WM_NOTIFY); CASE_STR(WM_INPUTLANGCHANGEREQUEST); CASE_STR(WM_INPUTLANGCHANGE); CASE_STR(WM_TCARD); CASE_STR(WM_HELP); CASE_STR(WM_USERCHANGED); CASE_STR(WM_NOTIFYFORMAT); CASE_STR(WM_CONTEXTMENU); CASE_STR(WM_STYLECHANGING); CASE_STR(WM_STYLECHANGED); CASE_STR(WM_DISPLAYCHANGE); CASE_STR(WM_GETICON); CASE_STR(WM_SETICON); CASE_STR(WM_NCCREATE); CASE_STR(WM_NCDESTROY); CASE_STR(WM_NCCALCSIZE); CASE_STR(WM_NCHITTEST); CASE_STR(WM_NCPAINT); CASE_STR(WM_NCACTIVATE); CASE_STR(WM_GETDLGCODE); CASE_STR(WM_SYNCPAINT); CASE_STR(WM_NCMOUSEMOVE); CASE_STR(WM_NCLBUTTONDOWN); CASE_STR(WM_NCLBUTTONUP); CASE_STR(WM_NCLBUTTONDBLCLK); CASE_STR(WM_NCRBUTTONDOWN); CASE_STR(WM_NCRBUTTONUP); CASE_STR(WM_NCRBUTTONDBLCLK); CASE_STR(WM_NCMBUTTONDOWN); CASE_STR(WM_NCMBUTTONUP); CASE_STR(WM_NCMBUTTONDBLCLK); CASE_STR(WM_NCXBUTTONDOWN); CASE_STR(WM_NCXBUTTONUP); CASE_STR(WM_NCXBUTTONDBLCLK); CASE_STR(WM_INPUT); CASE_STR(WM_KEYDOWN); CASE_STR(WM_KEYUP); CASE_STR(WM_CHAR); CASE_STR(WM_DEADCHAR); CASE_STR(WM_SYSKEYDOWN); CASE_STR(WM_SYSKEYUP); CASE_STR(WM_SYSCHAR); CASE_STR(WM_SYSDEADCHAR); CASE_STR(WM_KEYLAST); CASE_STR(WM_IME_STARTCOMPOSITION); CASE_STR(WM_IME_ENDCOMPOSITION); CASE_STR(WM_IME_COMPOSITION); CASE_STR(WM_INITDIALOG); CASE_STR(WM_COMMAND); CASE_STR(WM_SYSCOMMAND); CASE_STR(WM_TIMER); CASE_STR(WM_HSCROLL); CASE_STR(WM_VSCROLL); CASE_STR(WM_INITMENU); CASE_STR(WM_INITMENUPOPUP); CASE_STR(WM_GESTURE); CASE_STR(WM_GESTURENOTIFY); CASE_STR(WM_MENUSELECT); CASE_STR(WM_MENUCHAR); CASE_STR(WM_ENTERIDLE); CASE_STR(WM_UNINITMENUPOPUP); CASE_STR(WM_CHANGEUISTATE); CASE_STR(WM_UPDATEUISTATE); CASE_STR(WM_QUERYUISTATE); CASE_STR(WM_CTLCOLORMSGBOX); CASE_STR(WM_CTLCOLOREDIT); CASE_STR(WM_CTLCOLORLISTBOX); CASE_STR(WM_CTLCOLORBTN); CASE_STR(WM_CTLCOLORDLG); CASE_STR(WM_CTLCOLORSCROLLBAR); CASE_STR(WM_CTLCOLORSTATIC); CASE_STR(WM_MOUSEMOVE); CASE_STR(WM_LBUTTONDOWN); CASE_STR(WM_LBUTTONUP); CASE_STR(WM_LBUTTONDBLCLK); CASE_STR(WM_RBUTTONDOWN); CASE_STR(WM_RBUTTONUP); CASE_STR(WM_RBUTTONDBLCLK); CASE_STR(WM_MBUTTONDOWN); CASE_STR(WM_MBUTTONUP); CASE_STR(WM_MBUTTONDBLCLK); CASE_STR(WM_MOUSEWHEEL); CASE_STR(WM_XBUTTONDOWN); CASE_STR(WM_XBUTTONUP); CASE_STR(WM_XBUTTONDBLCLK); CASE_STR(WM_MOUSEHWHEEL); CASE_STR(WM_PARENTNOTIFY); CASE_STR(WM_ENTERMENULOOP); CASE_STR(WM_EXITMENULOOP); CASE_STR(WM_NEXTMENU); CASE_STR(WM_SIZING); CASE_STR(WM_CAPTURECHANGED); CASE_STR(WM_MOVING); CASE_STR(WM_POWERBROADCAST); CASE_STR(WM_DEVICECHANGE); CASE_STR(WM_POINTERDEVICECHANGE); CASE_STR(WM_POINTERDEVICEINRANGE); CASE_STR(WM_POINTERDEVICEOUTOFRANGE); CASE_STR(WM_POINTERUPDATE); CASE_STR(WM_POINTERDOWN); CASE_STR(WM_POINTERUP); CASE_STR(WM_POINTERENTER); CASE_STR(WM_POINTERLEAVE); CASE_STR(WM_POINTERACTIVATE); CASE_STR(WM_POINTERCAPTURECHANGED); CASE_STR(WM_IME_SETCONTEXT); CASE_STR(WM_IME_NOTIFY); CASE_STR(WM_IME_CONTROL); CASE_STR(WM_IME_COMPOSITIONFULL); CASE_STR(WM_IME_SELECT); CASE_STR(WM_IME_CHAR); CASE_STR(WM_IME_REQUEST); CASE_STR(WM_IME_KEYDOWN); CASE_STR(WM_IME_KEYUP); CASE_STR(WM_MDICREATE); CASE_STR(WM_MDIDESTROY); CASE_STR(WM_MDIACTIVATE); CASE_STR(WM_MDIRESTORE); CASE_STR(WM_MDINEXT); CASE_STR(WM_MDIMAXIMIZE); CASE_STR(WM_MDITILE); CASE_STR(WM_MDICASCADE); CASE_STR(WM_MDIICONARRANGE); CASE_STR(WM_MDIGETACTIVE); CASE_STR(WM_MDISETMENU); CASE_STR(WM_ENTERSIZEMOVE); CASE_STR(WM_EXITSIZEMOVE); CASE_STR(WM_DROPFILES); CASE_STR(WM_MDIREFRESHMENU); CASE_STR(WM_MOUSEHOVER); CASE_STR(WM_NCMOUSELEAVE); CASE_STR(WM_MOUSELEAVE); CASE_STR(WM_WTSSESSION_CHANGE); CASE_STR(WM_DPICHANGED); CASE_STR(WM_DPICHANGED_BEFOREPARENT); CASE_STR(WM_DPICHANGED_AFTERPARENT); CASE_STR(WM_CUT); CASE_STR(WM_COPY); CASE_STR(WM_PASTE); CASE_STR(WM_CLEAR); CASE_STR(WM_UNDO); CASE_STR(WM_RENDERFORMAT); CASE_STR(WM_RENDERALLFORMATS); CASE_STR(WM_DESTROYCLIPBOARD); CASE_STR(WM_DRAWCLIPBOARD); CASE_STR(WM_PAINTCLIPBOARD); CASE_STR(WM_VSCROLLCLIPBOARD); CASE_STR(WM_SIZECLIPBOARD); CASE_STR(WM_ASKCBFORMATNAME); CASE_STR(WM_CHANGECBCHAIN); CASE_STR(WM_HSCROLLCLIPBOARD); CASE_STR(WM_QUERYNEWPALETTE); CASE_STR(WM_PALETTEISCHANGING); CASE_STR(WM_PALETTECHANGED); CASE_STR(WM_HOTKEY); CASE_STR(WM_PRINT); CASE_STR(WM_PRINTCLIENT); CASE_STR(WM_APPCOMMAND); CASE_STR(WM_THEMECHANGED); CASE_STR(WM_DWMCOMPOSITIONCHANGED); CASE_STR(WM_DWMNCRENDERINGCHANGED); CASE_STR(WM_DWMCOLORIZATIONCOLORCHANGED); CASE_STR(WM_DWMWINDOWMAXIMIZEDCHANGE); CASE_STR(WM_HANDHELDFIRST); CASE_STR(WM_HANDHELDLAST); CASE_STR(WM_AFXFIRST); CASE_STR(WM_AFXLAST); CASE_STR(WM_PENWINFIRST); CASE_STR(WM_PENWINLAST); CASE_STR(WM_DWMSENDICONICTHUMBNAIL); CASE_STR(WM_DWMSENDICONICLIVEPREVIEWBITMAP); CASE_STR(WM_USER); CASE_STR(WM_APP); case 0x0118: return L"WM_SYSTIMER"; default: if (msg > WM_USER && msg < WM_APP) { CString text; text.Format(L"WM_USER + %d", msg - WM_USER); return text; } if (msg > WM_APP) { CString text; text.Format(L"WM_APP + %d", msg - WM_APP); return text; } break; } return L""; } ULONG_PTR WindowHelper::GetID(HWND hWnd) { return ::GetWindowLongPtr(hWnd, GWLP_ID); } ULONG_PTR WindowHelper::GetUserData(HWND hWnd) { return ::GetWindowLongPtr(hWnd, GWLP_USERDATA); } ================================================ FILE: WinSpy/WindowHelper.h ================================================ #pragma once struct WindowItem { HWND hWnd; DWORD ThreadId; DWORD ProcessId; CString ProcessName; }; struct StyleItem { DWORD Value; PCWSTR Text; DWORD Mask{ Value }; }; struct WindowHelper abstract final { static CString WindowStyleToString(HWND hWnd); static CString ClassStyleToString(HWND hWnd); static CString WindowExtendedStyleToString(HWND hWnd); static CString WindowRectToString(HWND hWnd); static CString GetWindowClassName(HWND hWnd); static CString GetWindowText(HWND hWnd); static HICON GetWindowIcon(HWND hWnd); static HICON GetWindowOrProcessIcon(HWND hWnd); static bool Flash(HWND hWnd); static void HighlightBorder(HWND hWnd, bool highlight = true); static CString GetWindowClassAndTitle(HWND hWnd); static std::unordered_map& GetIconMap(); static CImageList& GetImageList(); static WindowItem GetWindowInfo(HWND hWnd); static bool ThreadHasWindows(DWORD tid); static int ShowWindowProperties(HWND hWnd); static CString WindowMessageToString(DWORD msg); static ULONG_PTR GetID(HWND hWnd); static ULONG_PTR GetUserData(HWND hWnd); static std::pair GetWindowStyleArray(); static std::pair GetListViewStyleArray(); static std::pair GetTreeViewStyleArray(); static std::pair GetTabCtrlStyleArray(); static std::pair GetWindowStyleExArray(); static std::pair GetClassStyleArray(); static std::pair GetEditStyleArray(); static std::pair GetListBoxStyleArray(); static std::pair GetComboBoxStyleArray(); static std::pair GetHeaderStyleArray(); static std::pair GetButtonStyleArray(); static std::pair GetStaticStyleArray(); static std::pair GetToolTipStyleArray(); static std::pair GetStatusBarStyleArray(); static std::pair GetToolBarStyleArray(); static std::pair GetRebarStyleArray(); static std::pair GetScrollBarStyleArray(); }; ================================================ FILE: WinSpy/WindowWindowsPage.cpp ================================================ #include "pch.h" #include "resource.h" #include "WindowWindowsPage.h" #include "FormatHelper.h" const UINT WM_UPDATE = WM_USER + 300; void CWindowWindowsPage::UpdateData() { const struct { CWindow win; UINT idHandle, idClass, idText; } windows[] = { { m_Win.GetParent(), IDC_HANDLE, IDC_CLASS, IDC_TEXT }, { m_Win.GetWindow(GW_HWNDNEXT), IDC_HANDLE2, IDC_CLASS2, IDC_TEXT2 }, { m_Win.GetWindow(GW_HWNDPREV), IDC_HANDLE3, IDC_CLASS3, IDC_TEXT3 }, { m_Win.GetWindow(GW_CHILD), IDC_HANDLE4, IDC_CLASS4, IDC_TEXT4 }, }; CString text; for (auto& item : windows) { if (!::IsWindow(item.win.m_hWnd)) { SetDlgItemText(item.idHandle, L"(none) "); continue; } text.Format(L"0x%zX", (ULONG_PTR)item.win.m_hWnd); SetDlgItemText(item.idHandle, text); if (::GetClassName(item.win, text.GetBufferSetLength(128), 128)) SetDlgItemText(item.idClass, text); item.win.GetWindowText(text); SetDlgItemText(item.idText, text); } } LRESULT CWindowWindowsPage::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) { UpdateData(); return 0; } LRESULT CWindowWindowsPage::OnClickHandle(int, LPNMHDR hdr, BOOL&) { auto id = (UINT)hdr->idFrom; CString text; GetDlgItemText(id, text); auto hWnd = (HWND)FormatHelper::ParseHex(text.Mid(3, text.GetLength() - 6)); m_Win.Detach(); m_Win.Attach(hWnd); UpdateData(); GetParent().SendMessageToDescendants(WM_UPDATE, 0, reinterpret_cast(hWnd)); return 0; } ================================================ FILE: WinSpy/WindowWindowsPage.h ================================================ #pragma once #include "DialogHelper.h" class CWindowWindowsPage : public CDialogHelper, public CPropertyPageImpl { public: enum { IDD = IDD_PROPWINDOWS }; CWindowWindowsPage(HWND hWnd) : m_Win(hWnd) { m_psp.dwFlags |= PSP_USEICONID; m_psp.pszIcon = MAKEINTRESOURCE(IDI_WINDOWS); } protected: BEGIN_MSG_MAP(CWindowWindowsPage) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) NOTIFY_CODE_HANDLER(NM_CLICK, OnClickHandle) NOTIFY_CODE_HANDLER(NM_RETURN, OnClickHandle) END_MSG_MAP() private: void UpdateData(); // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnClickHandle(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); CWindow m_Win; }; ================================================ FILE: WinSpy/WindowsListView.cpp ================================================ #include "pch.h" #include "resource.h" #include "WindowsListView.h" #include "FormatHelper.h" #include "SortHelper.h" #include "ProcessHelper.h" void CWindowsListView::SetSelectedHwnd(HWND hWnd) { m_SelectedHwnd.Detach(); m_SelectedHwnd.Attach(hWnd); } LRESULT CWindowsListView::OnCreate(UINT, WPARAM, LPARAM, BOOL&) { m_hWndClient = m_List.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | LVS_SHAREIMAGELISTS, 0); m_List.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP); m_List.SetImageList(WindowHelper::GetImageList(), LVSIL_SMALL); auto cm = GetColumnManager(m_List); struct { PCWSTR text; DataItemType type; int width = 100; int format = LVCFMT_LEFT; ColumnFlags flags = ColumnFlags::Visible; } columns[] = { { L"Class Name", DataItemType::ClassName, 140 }, { L"Handle", DataItemType::Handle, 80, LVCFMT_RIGHT }, { L"Text", DataItemType::Text, 150 }, { L"Style", DataItemType::Style, 80, LVCFMT_RIGHT }, { L"Ex Style", DataItemType::ExtendedStyle, 80, LVCFMT_RIGHT }, { L"PID", DataItemType::ProcessId, 70, LVCFMT_RIGHT }, { L"Process Name", DataItemType::ProcessName, 120 }, { L"TID", DataItemType::ThreadId, 70, LVCFMT_RIGHT }, { L"Rectangle", DataItemType::Rectangle, 170, LVCFMT_LEFT, ColumnFlags::None }, { L"Parent HWND", DataItemType::ParentWindow, 100, LVCFMT_RIGHT, ColumnFlags::Fixed | ColumnFlags::Visible }, { L"Owner HWND", DataItemType::OwnerWindow, 100, LVCFMT_RIGHT, ColumnFlags::Fixed | ColumnFlags::Visible }, { L"Next HWND", DataItemType::NextWindow, 100, LVCFMT_RIGHT, ColumnFlags::Fixed | ColumnFlags::Visible }, { L"Prev HWND", DataItemType::PrevWindow, 100, LVCFMT_RIGHT, ColumnFlags::Fixed | ColumnFlags::Visible }, { L"Child HWND", DataItemType::FirstChildWindow, 100, LVCFMT_RIGHT, ColumnFlags::Fixed | ColumnFlags::Visible }, { L"WndProc", DataItemType::WindowProc, 100, LVCFMT_RIGHT, ColumnFlags::Fixed }, { L"User Data", DataItemType::UserData, 100, LVCFMT_RIGHT }, { L"ID", DataItemType::ID, 80, LVCFMT_RIGHT }, { L"Class Atom", DataItemType::ClassAtom, 90, LVCFMT_RIGHT }, { L"Class Style", DataItemType::ClassStyle, 80, LVCFMT_RIGHT, ColumnFlags::Fixed }, { L"Class Extra Bytes", DataItemType::ClassExtra, 70, LVCFMT_RIGHT, ColumnFlags::Fixed }, { L"Window Extra Bytes", DataItemType::WindowExtra, 70, LVCFMT_RIGHT, ColumnFlags::Fixed }, }; for (auto& col : columns) { cm->AddColumn(col.text, col.format, col.width, col.type, col.flags); } cm->UpdateColumns(); return 0; } LRESULT CWindowsListView::OnWindowShow(WORD, WORD, HWND, BOOL&) { CWindow win(m_Items[m_List.GetSelectionMark()].hWnd); win.ShowWindowAsync(SW_SHOW); return 0; } LRESULT CWindowsListView::OnWindowHide(WORD, WORD, HWND, BOOL&) { ATLASSERT(m_SelectedHwnd); m_SelectedHwnd.ShowWindowAsync(SW_HIDE); return 0; } LRESULT CWindowsListView::OnWindowMinimize(WORD, WORD, HWND, BOOL&) { CWindow win(m_Items[m_List.GetSelectionMark()].hWnd); win.ShowWindowAsync(SW_MINIMIZE); return 0; } LRESULT CWindowsListView::OnWindowMaximize(WORD, WORD, HWND, BOOL&) { CWindow win(m_Items[m_List.GetSelectionMark()].hWnd); win.ShowWindowAsync(SW_MAXIMIZE); return 0; } CString CWindowsListView::GetColumnText(HWND, int row, int col) const { auto& item = m_Items[row]; if (!::IsWindow(item.hWnd)) { return L""; } CWindow win(item.hWnd); auto h = item.hWnd; CString text; switch (GetColumnManager(m_List)->GetColumnTag(col)) { case DataItemType::Handle: text.Format(L"0x%zX", (ULONG_PTR)h); break; case DataItemType::Style: text.Format(L"0x%08X", win.GetStyle()); break; case DataItemType::ProcessId: text.Format(L"%u", item.ProcessId); break; case DataItemType::ProcessName: return item.ProcessName; case DataItemType::ThreadId: text.Format(L"%u", item.ThreadId); break; case DataItemType::Rectangle: return WindowHelper::WindowRectToString(h); case DataItemType::ExtendedStyle: text.Format(L"0x%08X", win.GetExStyle()); break; case DataItemType::ClassName: return WindowHelper::GetWindowClassName(h); case DataItemType::Text: win.GetWindowText(text); if (text.GetLength() > 256) text = text.Left(256) + L"..."; break; case DataItemType::WindowProc: text.Format(L"0x%zX", win.GetWindowLongPtr(GWLP_WNDPROC)); break; case DataItemType::UserData: text.Format(L"0x%zX", win.GetWindowLongPtr(GWLP_USERDATA)); break; case DataItemType::ID: text.Format(L"0x%zX", win.GetWindowLongPtr(GWLP_ID)); break; case DataItemType::ParentWindow: return FormatHelper::FormatHWndOrNone(::GetAncestor(win, GA_PARENT)); case DataItemType::NextWindow: return FormatHelper::FormatHWndOrNone(win.GetWindow(GW_HWNDNEXT)); case DataItemType::PrevWindow: return FormatHelper::FormatHWndOrNone(win.GetWindow(GW_HWNDPREV)); case DataItemType::OwnerWindow: return FormatHelper::FormatHWndOrNone(win.GetWindow(GW_OWNER)); case DataItemType::FirstChildWindow: return FormatHelper::FormatHWndOrNone(win.GetWindow(GW_CHILD)); case DataItemType::ClassAtom: text.Format(L"0x%04X", (DWORD)::GetClassLongPtr(win, GCW_ATOM)); break; case DataItemType::ClassStyle: text.Format(L"0x%04X", (DWORD)::GetClassLongPtr(win, GCL_STYLE)); break; case DataItemType::ClassExtra: text.Format(L"%u", (ULONG)::GetClassLongPtr(win, GCL_CBCLSEXTRA)); break; case DataItemType::WindowExtra: text.Format(L"%u", (ULONG)::GetClassLongPtr(win, GCL_CBWNDEXTRA)); break; } return text; } int CWindowsListView::GetRowImage(HWND, int row, int col) const { auto& item = m_Items[row]; auto h = item.hWnd; auto& icons = WindowHelper::GetIconMap(); if (auto it = icons.find(h); it != icons.end()) { return it->second; } auto hIcon = WindowHelper::GetWindowIcon(h); if (hIcon) { int image = WindowHelper::GetImageList().AddIcon(hIcon); icons.insert({ h, image }); return image; } return 0; } void CWindowsListView::DoSort(const SortInfo* si) { if (si == nullptr) return; std::sort(m_Items.begin(), m_Items.end(), [&](const auto& h1, const auto& h2) -> bool { switch (GetColumnManager(m_List)->GetColumnTag(si->SortColumn)) { case DataItemType::ClassName: return SortHelper::Sort(WindowHelper::GetWindowClassName(h1.hWnd), WindowHelper::GetWindowClassName(h2.hWnd), si->SortAscending); case DataItemType::Text: return SortHelper::Sort(WindowHelper::GetWindowText(h1.hWnd), WindowHelper::GetWindowText(h2.hWnd), si->SortAscending); case DataItemType::Handle: return SortHelper::Sort(h1.hWnd, h2.hWnd, si->SortAscending); case DataItemType::FirstChildWindow: return SortHelper::Sort(::GetWindow(h1.hWnd, GW_CHILD), ::GetWindow(h2.hWnd, GW_CHILD), si->SortAscending); case DataItemType::PrevWindow: return SortHelper::Sort(::GetWindow(h1.hWnd, GW_HWNDPREV), ::GetWindow(h2.hWnd, GW_HWNDPREV), si->SortAscending); case DataItemType::NextWindow: return SortHelper::Sort(::GetWindow(h1.hWnd, GW_HWNDNEXT), ::GetWindow(h2.hWnd, GW_HWNDNEXT), si->SortAscending); case DataItemType::OwnerWindow: return SortHelper::Sort(::GetWindow(h1.hWnd, GW_OWNER), ::GetWindow(h2.hWnd, GW_OWNER), si->SortAscending); case DataItemType::ParentWindow: return SortHelper::Sort(::GetParent(h1.hWnd), ::GetParent(h2.hWnd), si->SortAscending); case DataItemType::Style: return SortHelper::Sort(CWindow(h1.hWnd).GetStyle(), CWindow(h2.hWnd).GetStyle(), si->SortAscending); case DataItemType::ExtendedStyle: return SortHelper::Sort(CWindow(h1.hWnd).GetExStyle(), CWindow(h2.hWnd).GetExStyle(), si->SortAscending); case DataItemType::ThreadId: return SortHelper::Sort(h1.ThreadId, h2.ThreadId, si->SortAscending); case DataItemType::ProcessId: return SortHelper::Sort(h1.ProcessId, h2.ProcessId, si->SortAscending); case DataItemType::ProcessName: return SortHelper::Sort(h1.ProcessName, h2.ProcessName, si->SortAscending); case DataItemType::ID: return SortHelper::Sort(WindowHelper::GetID(h1.hWnd), WindowHelper::GetID(h2.hWnd), si->SortAscending); case DataItemType::UserData: return SortHelper::Sort(WindowHelper::GetUserData(h1.hWnd), WindowHelper::GetUserData(h2.hWnd), si->SortAscending); } return false; }); } bool CWindowsListView::OnRightClickList(HWND, int row, int col, CPoint const& pt) { CMenu menu; menu.LoadMenu(IDR_CONTEXT); m_ContextMenuOpen = true; auto cmd = m_pFrame->ShowPopupMenu(menu.GetSubMenu(0), pt, TPM_RETURNCMD); m_ContextMenuOpen = false; if (cmd) { LRESULT result; return ProcessWindowMessage(m_hWnd, WM_COMMAND, cmd, 0, result, 1); } return false; } bool CWindowsListView::IsSortable(HWND, int col) const { return true; } DWORD CWindowsListView::OnPrePaint(int, LPNMCUSTOMDRAW cd) { if (cd->hdr.hwndFrom == m_List) return CDRF_NOTIFYITEMDRAW; return CDRF_DODEFAULT; } DWORD CWindowsListView::OnItemPrePaint(int, LPNMCUSTOMDRAW cd) { ATLASSERT(cd->hdr.hwndFrom == m_List); auto& h = m_Items[(int)cd->dwItemSpec]; auto lv = (LPNMLVCUSTOMDRAW)cd; lv->clrTextBk = m_SelectedHwnd == nullptr || ::IsWindowVisible(h.hWnd) ? CLR_INVALID : RGB(224, 224, 224); return CDRF_DODEFAULT; } void CWindowsListView::AddMessageOnlyWindows() { HWND hWnd = nullptr; for (;;) { hWnd = ::FindWindowEx(HWND_MESSAGE, hWnd, nullptr, nullptr); if (!hWnd) break; m_Items.push_back(WindowHelper::GetWindowInfo(hWnd)); } } void CWindowsListView::UpdateList(HWND hWnd) { if (m_ContextMenuOpen) return; m_Items.clear(); m_SelectedHwnd = hWnd; if (hWnd == nullptr) { // // message only windows // AddMessageOnlyWindows(); } else { m_Items.push_back(WindowHelper::GetWindowInfo(m_SelectedHwnd)); AddChildWindows(m_Items, m_SelectedHwnd, true); } m_List.SetItemCountEx((int)m_Items.size(), LVSICF_NOSCROLL); UpdateList(); } void CWindowsListView::UpdateListByThread(DWORD tid) { m_SelectedHwnd.Detach(); m_Items.clear(); AddThreadWindows(tid); m_List.SetItemCountEx((int)m_Items.size(), LVSICF_NOSCROLL); UpdateList(); } void CWindowsListView::UpdateListByProcess(ProcessInfo const& pi) { m_Items.clear(); for (auto tid : pi.Threads) AddThreadWindows(tid); m_List.SetItemCountEx((int)m_Items.size(), LVSICF_NOSCROLL); UpdateList(); } void CWindowsListView::UpdateUI(CUpdateUIBase& ui) { if (::GetFocus() != m_List) return; int first = m_List.GetSelectionMark(); int selected = m_List.GetSelectedCount(); ui.UIEnable(ID_WINDOW_PROPERTIES, selected == 1); } void CWindowsListView::AddThreadWindows(DWORD tid) { ::EnumThreadWindows(tid, [](auto hWnd, auto param) { auto p = reinterpret_cast(param); p->m_Items.push_back(WindowHelper::GetWindowInfo(hWnd)); return TRUE; }, reinterpret_cast(this)); } void CWindowsListView::Refresh() { } void CWindowsListView::AddChildWindows(std::vector& v, HWND hParent, bool directOnly) { struct LocalInfo { std::vector& v; CWindowsListView* pThis; bool directOnly; }; LocalInfo info{ v, this, directOnly }; ::EnumChildWindows(hParent, [](auto hWnd, auto param) { auto info = reinterpret_cast(param); if (info->pThis->m_ShowHiddenWindows || ::IsWindowVisible(hWnd)) { info->v.push_back(WindowHelper::GetWindowInfo(hWnd)); if (!info->directOnly) info->pThis->AddChildWindows(info->v, hWnd, false); } return TRUE; }, reinterpret_cast(&info)); } CString CWindowsListView::GetDetails(const DataItem& item) const { CString text; switch (item.Type) { case DataItemType::ProcessId: { DWORD pid = 0; ::GetWindowThreadProcessId(m_SelectedHwnd, &pid); return ProcessHelper::GetProcessImageName(pid); } case DataItemType::Text: text.Format(L"Length: %d", m_SelectedHwnd.GetWindowTextLength()); break; return WindowHelper::WindowStyleToString(m_SelectedHwnd); case DataItemType::Style: return WindowHelper::WindowStyleToString(m_SelectedHwnd); case DataItemType::ClassStyle: return WindowHelper::ClassStyleToString(m_SelectedHwnd); case DataItemType::ExtendedStyle: return WindowHelper::WindowExtendedStyleToString(m_SelectedHwnd); case DataItemType::Rectangle: if (m_SelectedHwnd.IsZoomed()) return L"Maximized"; if (m_SelectedHwnd.IsIconic()) return L"Minimized"; break; case DataItemType::NextWindow: return WindowHelper::GetWindowClassAndTitle(m_SelectedHwnd.GetWindow(GW_HWNDNEXT)); case DataItemType::PrevWindow: return WindowHelper::GetWindowClassAndTitle(m_SelectedHwnd.GetWindow(GW_HWNDPREV)); case DataItemType::OwnerWindow: return WindowHelper::GetWindowClassAndTitle(m_SelectedHwnd.GetWindow(GW_OWNER)); case DataItemType::ParentWindow: return WindowHelper::GetWindowClassAndTitle(::GetAncestor(m_SelectedHwnd, GA_PARENT)); case DataItemType::FirstChildWindow: return WindowHelper::GetWindowClassAndTitle(m_SelectedHwnd.GetWindow(GW_CHILD)); } return text; } void CWindowsListView::UpdateList() { DoSort(GetSortInfo(m_List)); m_List.RedrawItems(m_List.GetTopIndex(), m_List.GetCountPerPage() + m_List.GetTopIndex()); } LRESULT CWindowsListView::OnToggleEmptyTitleWindows(WORD, WORD, HWND, BOOL&) { m_ShowNoTitleWindows = !m_ShowNoTitleWindows; UpdateList(m_SelectedHwnd); return 0; } LRESULT CWindowsListView::OnToggleChildWindows(WORD, WORD, HWND, BOOL&) { m_ShowChildWindows = !m_ShowChildWindows; UpdateList(m_SelectedHwnd); return 0; } LRESULT CWindowsListView::OnWindowRestore(WORD, WORD, HWND, BOOL&) { CWindow win(m_Items[m_List.GetSelectionMark()].hWnd); win.ShowWindowAsync(SW_RESTORE); return 0; } LRESULT CWindowsListView::OnWindowBringToFront(WORD, WORD, HWND, BOOL&) { CWindow win(m_Items[m_List.GetSelectionMark()].hWnd); win.BringWindowToTop(); return 0; } LRESULT CWindowsListView::OnWindowProperties(WORD, WORD, HWND, BOOL&) { auto& item = m_Items[m_List.GetSelectionMark()]; WindowHelper::ShowWindowProperties(item.hWnd); return LRESULT(); } LRESULT CWindowsListView::OnItemChanged(int, LPNMHDR, BOOL&) { UpdateUI(m_pFrame->GetUIUpdate()); return 0; } LRESULT CWindowsListView::OnWindowFlash(WORD, WORD, HWND, BOOL&) { auto& item = m_Items[m_List.GetSelectionMark()]; WindowHelper::Flash(item.hWnd); return 0; } LRESULT CWindowsListView::OnToggleHiddenWindows(WORD, WORD, HWND, BOOL&) { m_ShowHiddenWindows = !m_ShowHiddenWindows; UpdateList(m_SelectedHwnd); return 0; } bool CWindowsListView::OnDoubleClickList(HWND, int row, int col, CPoint const&) { if (row >= 0) { WindowHelper::ShowWindowProperties(m_Items[row].hWnd); return true; } return false; } ================================================ FILE: WinSpy/WindowsListView.h ================================================ #pragma once #include #include "Interfaces.h" #include "WindowHelper.h" #include "ProcessHelper.h" class CWindowsListView : public CFrameWindowImpl, public CVirtualListView, public CCustomDraw { public: using BaseFrame = CFrameWindowImpl; CWindowsListView(IMainFrame* frame) : m_pFrame(frame) {} CString GetColumnText(HWND, int row, int col) const; int GetRowImage(HWND, int row, int col) const; bool IsSortable(HWND, int col) const; void DoSort(const SortInfo* si); bool OnRightClickList(HWND, int row, int col, CPoint const&); bool OnDoubleClickList(HWND, int row, int col, CPoint const&); DWORD OnPrePaint(int, LPNMCUSTOMDRAW cd); DWORD OnItemPrePaint(int, LPNMCUSTOMDRAW cd); void SetSelectedHwnd(HWND hWnd); void UpdateList(HWND hWnd); void UpdateListByThread(DWORD tid); void UpdateListByProcess(ProcessInfo const& pi); void UpdateUI(CUpdateUIBase& ui); void Refresh(); enum class DataItemType { Handle, ClassName, Text, Style, ExtendedStyle, ProcessName, ProcessId, ThreadId, ParentWindow, FirstChildWindow, NextWindow, PrevWindow, OwnerWindow, WindowProc, UserData, ID, Rectangle, ClassAtom, ClassStyle, ClassExtra, WindowExtra, }; struct DataItem { CString Property; DataItemType Type; }; protected: BEGIN_MSG_MAP(CWindowsListView) MESSAGE_HANDLER(WM_CREATE, OnCreate) NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnItemChanged) CHAIN_MSG_MAP(CVirtualListView) CHAIN_MSG_MAP(CCustomDraw) CHAIN_MSG_MAP(BaseFrame) ALT_MSG_MAP(1) if (::GetFocus() != m_List) return FALSE; COMMAND_ID_HANDLER(ID_WINDOW_SHOW, OnWindowShow) COMMAND_ID_HANDLER(ID_WINDOW_HIDE, OnWindowHide) COMMAND_ID_HANDLER(ID_WINDOW_BRINGTOFRONT, OnWindowBringToFront) COMMAND_ID_HANDLER(ID_WINDOW_MINIMIZE, OnWindowMinimize) COMMAND_ID_HANDLER(ID_WINDOW_MAXIMIZE, OnWindowMaximize) COMMAND_ID_HANDLER(ID_STATE_FLASH, OnWindowFlash) COMMAND_ID_HANDLER(ID_WINDOW_RESTORE, OnWindowRestore) COMMAND_ID_HANDLER(ID_VIEW_HIDDENWINDOWS, OnToggleHiddenWindows) COMMAND_ID_HANDLER(ID_VIEW_EMPTYTITLEWINDOWS, OnToggleEmptyTitleWindows) COMMAND_ID_HANDLER(ID_VIEW_CHILDWINDOWS, OnToggleChildWindows) COMMAND_ID_HANDLER(ID_WINDOW_PROPERTIES, OnWindowProperties) END_MSG_MAP() private: void AddChildWindows(std::vector& v, HWND hParent, bool directOnly); void AddThreadWindows(DWORD tid); CString GetDetails(const DataItem& item) const; void UpdateList(); void AddMessageOnlyWindows(); LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnWindowShow(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowHide(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowMinimize(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowMaximize(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowRestore(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowFlash(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleHiddenWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleEmptyTitleWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleChildWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowBringToFront(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnItemChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); private: CListViewCtrl m_List; std::vector m_Items; DWORD m_TotalWindows, m_TotalVisibleWindows, m_TopLevelWindows; CWindow m_SelectedHwnd; IMainFrame* m_pFrame; bool m_ShowHiddenWindows : 1 { false }; bool m_ShowNoTitleWindows : 1 { true }; bool m_ShowChildWindows : 1 { true }; bool m_Deleting : 1{ false }; bool m_ContextMenuOpen : 1{ false }; }; ================================================ FILE: WinSpy/WindowsView.cpp ================================================ // View.cpp : implementation of the CWindowsView class // ///////////////////////////////////////////////////////////////////////////// #include "pch.h" #include "resource.h" #include "FormatHelper.h" #include "WindowsView.h" #include "ProcessHelper.h" #include "WindowHelper.h" #include "SortHelper.h" #include "MessagesView.h" BOOL CWindowsView::PreTranslateMessage(MSG* pMsg) { pMsg; return FALSE; } void CWindowsView::OnActivate(bool activate) { if (activate) { UpdateUI(); SetTimer(1, 2000, nullptr); } else { KillTimer(1); } } void CWindowsView::UpdateUI() { auto& ui = GetFrame()->GetUIUpdate(); if (::GetFocus() == m_Tree) { ui.UISetCheck(ID_VIEW_HIDDENWINDOWS, m_ShowHiddenWindows); ui.UISetCheck(ID_VIEW_EMPTYTITLEWINDOWS, m_ShowNoTitleWindows); ui.UIEnable(ID_WINDOW_PROPERTIES, m_SelectedHwnd != nullptr); //ui.UISetCheck(ID_VIEW_CHILDWINDOWS, m_ShowChildWindows); } else { m_WindowsView.UpdateUI(ui); } } void CWindowsView::Refresh() { if (m_SelectedHwnd != nullptr && !m_SelectedHwnd.IsWindow()) { m_Tree.DeleteItem(m_Selected); return; } m_WindowsView.UpdateList(m_SelectedHwnd); } LRESULT CWindowsView::OnRefreshAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { InitTree(); return 0; } void CWindowsView::InitTree() { m_TotalVisibleWindows = m_TotalWindows = m_TopLevelWindows = 0; m_DesktopNode = nullptr; HWND hDesktop = ::GetDesktopWindow(); if (!hDesktop) return; CWaitCursor wait; m_Tree.LockWindowUpdate(TRUE); m_Deleting = true; m_Tree.DeleteAllItems(); m_WindowMap.clear(); m_Deleting = false; m_DesktopNode = AddNode(hDesktop, TVI_ROOT); ::EnumWindows([](auto hWnd, auto lp) -> BOOL { auto pThis = (CWindowsView*)lp; pThis->AddNode(hWnd, pThis->m_DesktopNode); return TRUE; }, reinterpret_cast(this)); AddMessageOnlyWindows(); m_DesktopNode.Expand(TVE_EXPAND); m_Tree.LockWindowUpdate(FALSE); m_DesktopNode.Select(); m_DesktopNode.EnsureVisible(); m_Tree.SetScrollPos(SB_HORZ, 0); } void CWindowsView::AddChildWindows(HTREEITEM hParent) { auto hWnd = (HWND)m_Tree.GetItemData(hParent); ATLASSERT(hWnd); m_hCurrentNode = hParent; ::EnumChildWindows(hWnd, [](auto hChild, auto p) -> BOOL { auto pThis = (CWindowsView*)p; return pThis->AddChildNode(hChild); }, reinterpret_cast(this)); } CTreeItem CWindowsView::AddNode(HWND hWnd, HTREEITEM hParent) { CString text, name; CWindow win(hWnd); m_TotalWindows++; if (win.IsWindowVisible()) m_TotalVisibleWindows++; if (m_DesktopNode) { if (::GetAncestor(hWnd, GA_PARENT) == (HWND)m_DesktopNode.GetData()) m_TopLevelWindows++; if (!m_ShowHiddenWindows && !win.IsWindowVisible()) return nullptr; win.GetWindowText(name); if (!m_ShowNoTitleWindows && name.IsEmpty()) return nullptr; } if (name.GetLength() > 64) name = name.Left(64) + L"..."; if (!name.IsEmpty()) name = L"[" + name + L"]"; WCHAR className[64] = { 0 }; ::GetClassName(hWnd, className, _countof(className)); text.Format(L"0x%zX (%s) %s", (DWORD_PTR)hWnd, className, (PCWSTR)name); HICON hIcon{ nullptr }; int image = 0; if ((win.GetStyle() & WS_CHILD) == 0) { auto& icons = WindowHelper::GetIconMap(); if (auto it = icons.find(hWnd); it == icons.end()) { hIcon = WindowHelper::GetWindowOrProcessIcon(hWnd); if (hIcon) { icons.insert({ hWnd, image = WindowHelper::GetImageList().AddIcon(hIcon) }); } } else { image = it->second; } } auto node = m_Tree.InsertItem(text, image, image, hParent, TVI_LAST); node.SetData((DWORD_PTR)hWnd); m_WindowMap.insert({ hWnd, node }); if (!win.IsWindowVisible()) node.SetState(TVIS_CUT, TVIS_CUT); if (m_DesktopNode && win.GetWindow(GW_CHILD)) { // add a "plus" button node.AddTail(L"*", 0); } return node; } BOOL CWindowsView::AddChildNode(HWND hChild) { if (::GetAncestor(hChild, GA_PARENT) == (HWND)m_Tree.GetItemData(m_hCurrentNode)) { AddNode(hChild, m_hCurrentNode); } return TRUE; } LRESULT CWindowsView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { m_Splitter.SetSplitterExtendedStyle(SPLIT_FLATBAR | SPLIT_PROPORTIONAL); m_hWndClient = m_Splitter.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); //m_TreeFrame.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); //m_Tree.Attach(m_TreeFrame.m_hWndClient); //ToolBarButtonInfo buttons[] = { // { ID_VIEW_REFRESH, IDI_REFRESH }, // { 0 }, // { ID_VIEW_HIDDENWINDOWS, IDI_WINDOW_HIDDEN }, // { ID_VIEW_EMPTYTITLEWINDOWS, IDI_WINDOW_NOTEXT }, // { ID_WINDOW_PROPERTIES, IDI_WINPROP }, //}; //m_TreeFrame.CreateAndInitToolBar(buttons, _countof(buttons)); m_Tree.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS, 0); m_Tree.SetExtendedStyle(TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER); m_Tree.SetImageList(WindowHelper::GetImageList(), TVSIL_NORMAL); m_WindowsView.Create(m_Splitter, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); m_Splitter.SetSplitterPanes(m_Tree, m_WindowsView); UpdateLayout(); m_Splitter.SetSplitterPosPct(35); InitTree(); m_WindowMap.reserve(256); SetTimer(1, 2000, nullptr); return 0; } LRESULT CWindowsView::OnTimer(UINT, WPARAM id, LPARAM, BOOL&) { if (id == 1) Refresh(); else if (id == 3) { KillTimer(3); m_Selected = m_Tree.GetSelectedItem(); NodeSelected(); } return 0; } void CWindowsView::NodeSelected() { m_SelectedHwnd.Detach(); auto data = m_Selected.GetData(); auto hWnd = (HWND)data; if (data != MessageOnlyWindowsNode && !::IsWindow(hWnd)) // window is probably destroyed m_Selected.Delete(); else { if (data == MessageOnlyWindowsNode) m_WindowsView.UpdateList(nullptr); else { m_SelectedHwnd.Attach(hWnd); m_WindowsView.UpdateList(hWnd); } } UpdateUI(); } LRESULT CWindowsView::OnNodeExpanding(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; if (tv->action == TVE_EXPAND) { auto hItem = tv->itemNew.hItem; auto child = m_Tree.GetChildItem(hItem); if (child.GetData() == 0) { child.Delete(); AddChildWindows(hItem); } } return 0; } LRESULT CWindowsView::OnNodeDeleted(int, LPNMHDR hdr, BOOL&) { if (!m_Deleting) { auto tv = (NMTREEVIEW*)hdr; m_WindowMap.erase((HWND)tv->itemOld.lParam); } return 0; } LRESULT CWindowsView::OnNodeSelected(int, LPNMHDR hdr, BOOL&) { auto tv = (NMTREEVIEW*)hdr; if (tv->action == TVC_BYKEYBOARD) { // short delay before update in case the user moves quickly through the tree SetTimer(3, 250, nullptr); } else { m_Selected.m_hTreeItem = tv->itemNew.hItem; m_Selected.m_pTreeView = &m_Tree; NodeSelected(); } return 0; } LRESULT CWindowsView::OnWindowShow(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.ShowWindowAsync(SW_SHOW); return 0; } LRESULT CWindowsView::OnWindowHide(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.ShowWindowAsync(SW_HIDE); return 0; } LRESULT CWindowsView::OnWindowClose(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.PostMessage(WM_CLOSE); return 0; } LRESULT CWindowsView::OnWindowMinimize(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.ShowWindowAsync(SW_MINIMIZE); return 0; } LRESULT CWindowsView::OnWindowMaximize(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.ShowWindowAsync(SW_MAXIMIZE); return 0; } LRESULT CWindowsView::OnWindowRestore(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.ShowWindowAsync(SW_RESTORE); return 0; } LRESULT CWindowsView::OnWindowBringToFront(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) m_SelectedHwnd.BringWindowToTop(); return 0; } LRESULT CWindowsView::OnRefresh(WORD, WORD, HWND, BOOL&) { InitTree(); return 0; } LRESULT CWindowsView::OnToggleHiddenWindows(WORD, WORD, HWND, BOOL&) { m_ShowHiddenWindows = !m_ShowHiddenWindows; UpdateUI(); InitTree(); return 0; } LRESULT CWindowsView::OnToggleEmptyTitleWindows(WORD, WORD, HWND, BOOL&) { m_ShowNoTitleWindows = !m_ShowNoTitleWindows; UpdateUI(); InitTree(); return 0; } LRESULT CWindowsView::OnToggleChildWindows(WORD, WORD, HWND, BOOL&) { m_ShowChildWindows = !m_ShowChildWindows; UpdateUI(); InitTree(); return 0; } LRESULT CWindowsView::OnTreeNodeRightClick(HTREEITEM hItem, CPoint const& pt) { ATLASSERT(m_Selected); if (!m_Selected) return 0; CMenu menu; menu.LoadMenu(IDR_CONTEXT); return GetFrame()->ShowPopupMenu(menu.GetSubMenu(0), pt); } LRESULT CWindowsView::OnWindowFlash(WORD, WORD, HWND, BOOL&) { if (m_SelectedHwnd) WindowHelper::Flash(m_SelectedHwnd); return 0; } LRESULT CWindowsView::OnWindowHighlight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { if (m_SelectedHwnd) { ::TrySubmitThreadpoolCallback([](auto, auto p) { auto hWnd = (HWND)p; for (int i = 0; i < 3; i++) { WindowHelper::HighlightBorder(hWnd); ::Sleep(500); WindowHelper::HighlightBorder(hWnd, false); ::Sleep(500); } }, m_SelectedHwnd, nullptr); } return 0; } LRESULT CWindowsView::OnWindowProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& handled) { if (::GetFocus() != m_Tree) { handled = FALSE; return 0; } if (m_SelectedHwnd) { WindowHelper::ShowWindowProperties(m_SelectedHwnd); } return 0; } LRESULT CWindowsView::OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt) { WindowHelper::ShowWindowProperties((HWND)m_Tree.GetItemData(hItem)); return 1; } LRESULT CWindowsView::OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { m_Tree.SetFocus(); return 0; } CTreeItem CWindowsView::AddMessageOnlyWindows() { m_MsgOnlyNode = m_Tree.InsertItem(L"Message Only Windows", TVI_ROOT, TVI_LAST); m_MsgOnlyNode.SetData(MessageOnlyWindowsNode); return m_MsgOnlyNode; } LRESULT CWindowsView::OnCaptureMessages(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { if (m_SelectedHwnd) { auto view = GetFrame()->CreateMessagesView(); if (!view->CaptureWindow(m_SelectedHwnd)) { AtlMessageBox(m_hWnd, L"Failed to register hook", IDS_TITLE, MB_ICONERROR); GetFrame()->CloseTab(view); } } return 0; } ================================================ FILE: WinSpy/WindowsView.h ================================================ // View.h : interface of the CView class // ///////////////////////////////////////////////////////////////////////////// #pragma once #include "TreeViewManager.h" #include "ViewBase.h" #include "WindowsListView.h" #include class CWindowsView : public CViewBase, public CTreeViewManager { public: CWindowsView(IMainFrame* frame) : CViewBase(frame), m_WindowsView(frame) {} BOOL PreTranslateMessage(MSG* pMsg); void OnActivate(bool activate); LRESULT OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt); protected: enum { IDC_TREE = 123 }; BEGIN_MSG_MAP(CWindowsView) MESSAGE_HANDLER(WM_TIMER, OnTimer) MESSAGE_HANDLER(WM_CREATE, OnCreate) NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING, OnNodeExpanding) NOTIFY_CODE_HANDLER(TVN_DELETEITEM, OnNodeDeleted) NOTIFY_CODE_HANDLER(TVN_SELCHANGED, OnNodeSelected) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) COMMAND_ID_HANDLER(ID_VIEW_HIDDENWINDOWS, OnToggleHiddenWindows) COMMAND_ID_HANDLER(ID_VIEW_EMPTYTITLEWINDOWS, OnToggleEmptyTitleWindows) COMMAND_ID_HANDLER(ID_WINDOW_SHOW, OnWindowShow) COMMAND_ID_HANDLER(ID_WINDOW_HIDE, OnWindowHide) COMMAND_ID_HANDLER(ID_WINDOW_BRINGTOFRONT, OnWindowBringToFront) COMMAND_ID_HANDLER(ID_WINDOW_MINIMIZE, OnWindowMinimize) COMMAND_ID_HANDLER(ID_WINDOW_MAXIMIZE, OnWindowMaximize) COMMAND_ID_HANDLER(ID_WINDOW_HIGHLIGHT, OnWindowHighlight) COMMAND_ID_HANDLER(ID_STATE_FLASH, OnWindowFlash) COMMAND_ID_HANDLER(ID_WINDOW_RESTORE, OnWindowRestore) COMMAND_ID_HANDLER(ID_STATE_CLOSE, OnWindowClose) COMMAND_ID_HANDLER(ID_WINDOW_MESSAGES, OnCaptureMessages) COMMAND_ID_HANDLER(ID_WINDOW_PROPERTIES, OnWindowProperties) COMMAND_ID_HANDLER(ID_VIEW_REFRESH, OnRefreshAll) CHAIN_MSG_MAP(CTreeViewManager) CHAIN_MSG_MAP(CViewBase) if (m_WindowsView) { CHAIN_MSG_MAP_ALT_MEMBER(m_WindowsView, 1) } END_MSG_MAP() LRESULT OnTreeNodeRightClick(HTREEITEM hItem, CPoint const& pt); private: void UpdateUI(); void Refresh(); void InitTree(); void AddChildWindows(HTREEITEM hParent); CTreeItem AddNode(HWND hWnd, HTREEITEM hParent); BOOL AddChildNode(HWND hChild); void NodeSelected(); CTreeItem AddMessageOnlyWindows(); // Handler prototypes (uncomment arguments if needed): // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnNodeExpanding(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnNodeDeleted(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnNodeSelected(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); LRESULT OnRefreshAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowShow(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowHide(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowMinimize(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowMaximize(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowRestore(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowClose(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleHiddenWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleEmptyTitleWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnToggleChildWindows(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowFlash(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowBringToFront(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowProperties(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnWindowHighlight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); LRESULT OnCaptureMessages(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); enum { MessageOnlyWindowsNode = 1 }; CCustomSplitterWindow m_Splitter; CWindowsListView m_WindowsView; //CTreeViewFrame m_TreeFrame; CTreeViewCtrlEx m_Tree; CTreeItem m_hCurrentNode; CTreeItem m_DesktopNode; CTreeItem m_Selected; CTreeItem m_MsgOnlyNode; CWindow m_SelectedHwnd; std::unordered_map m_WindowMap; DWORD m_TotalWindows, m_TotalVisibleWindows, m_TopLevelWindows; bool m_ShowHiddenWindows : 1 { false }; bool m_ShowNoTitleWindows : 1 { true }; bool m_ShowChildWindows : 1 { true }; bool m_Deleting{ false }; }; ================================================ FILE: WinSpy/pch.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // WinSpy.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "pch.h" ================================================ FILE: WinSpy/pch.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once // Change these values to use different versions #define WINVER 0x0605 #define _WIN32_WINNT 0x0601 #define _WIN32_IE 0x0700 #define _RICHEDIT_VER 0x0500 #define _HAS_EXCEPTIONS 0 #include #include #include extern CAppModule _Module; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined _M_IX86 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_IA64 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_X64 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") #else #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #endif ================================================ FILE: WinSpy/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by WinSpy.rc // #define IDD_ABOUTBOX 100 #define IDD_PROPWINDOWS 107 #define IDR_MAINFRAME 128 #define IDS_TITLE 129 #define IDI_WINDOW 201 #define IDI_WINDOW_HIDDEN 202 #define IDI_WINDOW_DELETE 204 #define IDI_WINDOW_NEW 205 #define IDI_WINDOWS 206 #define IDI_REFRESH 207 #define IDR_CONTEXT 208 #define IDI_WINDOW_CLOSE 209 #define IDI_WINDOW_NOTEXT 210 #define IDD_FINDWINDOW 211 #define IDI_WINDOW_MINIMIZE 213 #define IDI_WINDOW_SENDTOBACK 214 #define IDI_WINDOW_MAXIMIZE 215 #define IDI_TARGET 216 #define IDI_PROCESSES 217 #define IDD_WINPROP 218 #define IDI_WINPROP 221 #define IDI_PROCESS_INFO 222 #define IDI_MESSAGE 223 #define IDI_MESSAGES 224 #define IDI_WINDOWSEARCH 225 #define IDI_SENDTOFRONT 227 #define IDI_RESTORE 228 #define IDI_ICON1 229 #define IDI_AUTOMATION 229 #define IDC_HANDLE 1000 #define IDC_CLASSNAME 1001 #define IDC_HANDLE2 1001 #define IDC_CLASSNAME2 1002 #define IDC_TEXT 1002 #define IDC_SEARCH 1003 #define IDC_TEXT2 1003 #define IDC_TARGET 1004 #define IDC_HANDLE3 1004 #define IDC_HIDE 1005 #define IDC_TEXT3 1005 #define IDC_THREAD 1006 #define IDC_HANDLE4 1006 #define IDC_PROCESS 1007 #define IDC_TEXT4 1007 #define IDC_RECT 1008 #define IDC_CLIENTRECT 1009 #define IDC_STYLE 1010 #define IDC_STYLES 1011 #define IDC_STYLEEX 1012 #define IDC_STYLESEX 1013 #define IDC_ATOM 1014 #define IDC_RESTORE_RECT 1015 #define IDC_MENU 1016 #define IDC_CLASS 1016 #define IDC_USERDATA 1017 #define IDC_CLASS2 1017 #define IDC_STYLECLASS 1018 #define IDC_CLASS3 1018 #define IDC_CLASSSTYLE 1019 #define IDC_CLASS4 1019 #define ID_WINDOW_CLOSE 32772 #define ID_WINDOW_CLOSE_ALL 32773 #define ID_VIEW_ALLWINDOWS 32775 #define ID_VIEW_ALLPROCESSES 32776 #define ID_VIEW_REFRESH32777 32777 #define ID_VIEW_REFRES 32778 #define ID_WINDOW_SHOW 32779 #define ID_WINDOW_HIDE 32780 #define ID_WINDOW_HIGHLIGHT 32781 #define ID_WINDOW_MINIMIZE 32782 #define ID_WINDOW_MAXIMIZE 32783 #define ID_WINDOW_RESTORE 32784 #define ID_WINDOW_BRINGTOFRONT 32785 #define ID_WINDOW_PROPERTIES 32786 #define ID_WINDOW_FIND 32787 #define ID_WINDOW_STATE 32788 #define ID_OPTIONS_ALWAYSONTOP 32789 #define ID_VIEW_HIDDENWINDOWS 32790 #define ID_VIEW_EMPTYTITLEWINDOWS 32791 #define ID_STATE_CLOSE 32792 #define ID_STATE_FLASH 32793 #define ID_VIEW_CHILDWINDOWS 32794 #define ID_VIEW_WINDOWSLIST 32795 #define ID_LIST_LOCATEINTREE 32796 #define ID_WINDOW_LOCATEINTREE 32797 #define ID_TREE_SENDTOBACK 32798 #define ID_FILE_RUNASADMINISTRATOR 32799 #define ID_OPTIONS_FONTS 32800 #define ID_PROCESS_TERMINATE 32801 #define ID_PROCESS_PROPERTIES 32802 #define ID_THREAD_PROPERTIES 32803 #define ID_WINDOW_MESSAGES 32804 #define ID_VIEW_AUTOMATIONTREE 32805 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 230 #define _APS_NEXT_COMMAND_VALUE 32806 #define _APS_NEXT_CONTROL_VALUE 1019 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: WinSpy.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32519.111 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinSpy", "WinSpy\WinSpy.vcxproj", "{3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinSpyHook", "WinSpyHook\WinSpyHook.vcxproj", "{1CA31FEE-1F81-4C94-9205-D908841958DD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WTLHelper", "WTLHelper\WTLHelper\WTLHelper.vcxproj", "{AE53419F-A769-4548-8E15-E311904DF7DF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 ReleaseSigned|ARM64 = ReleaseSigned|ARM64 ReleaseSigned|x64 = ReleaseSigned|x64 ReleaseSigned|x86 = ReleaseSigned|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Debug|ARM64.ActiveCfg = Debug|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Debug|ARM64.Build.0 = Debug|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Debug|x64.ActiveCfg = Debug|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Debug|x64.Build.0 = Debug|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Debug|x86.ActiveCfg = Debug|Win32 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Debug|x86.Build.0 = Debug|Win32 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Release|ARM64.ActiveCfg = Release|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Release|ARM64.Build.0 = Release|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Release|x64.ActiveCfg = Release|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Release|x64.Build.0 = Release|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Release|x86.ActiveCfg = Release|Win32 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.Release|x86.Build.0 = Release|Win32 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.ReleaseSigned|ARM64.ActiveCfg = ReleaseSigned|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.ReleaseSigned|ARM64.Build.0 = ReleaseSigned|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.ReleaseSigned|x64.ActiveCfg = ReleaseSigned|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.ReleaseSigned|x64.Build.0 = ReleaseSigned|x64 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.ReleaseSigned|x86.ActiveCfg = ReleaseSigned|Win32 {3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}.ReleaseSigned|x86.Build.0 = ReleaseSigned|Win32 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Debug|ARM64.ActiveCfg = Debug|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Debug|ARM64.Build.0 = Debug|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Debug|x64.ActiveCfg = Debug|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Debug|x64.Build.0 = Debug|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Debug|x86.ActiveCfg = Debug|Win32 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Debug|x86.Build.0 = Debug|Win32 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Release|ARM64.ActiveCfg = Release|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Release|ARM64.Build.0 = Release|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Release|x64.ActiveCfg = Release|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Release|x64.Build.0 = Release|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Release|x86.ActiveCfg = Release|Win32 {1CA31FEE-1F81-4C94-9205-D908841958DD}.Release|x86.Build.0 = Release|Win32 {1CA31FEE-1F81-4C94-9205-D908841958DD}.ReleaseSigned|ARM64.ActiveCfg = ReleaseSigned|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.ReleaseSigned|ARM64.Build.0 = ReleaseSigned|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.ReleaseSigned|x64.ActiveCfg = ReleaseSigned|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.ReleaseSigned|x64.Build.0 = ReleaseSigned|x64 {1CA31FEE-1F81-4C94-9205-D908841958DD}.ReleaseSigned|x86.ActiveCfg = ReleaseSigned|Win32 {1CA31FEE-1F81-4C94-9205-D908841958DD}.ReleaseSigned|x86.Build.0 = ReleaseSigned|Win32 {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|ARM64.ActiveCfg = Debug|ARM64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|ARM64.Build.0 = Debug|ARM64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x64.ActiveCfg = Debug|x64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x64.Build.0 = Debug|x64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x86.ActiveCfg = Debug|Win32 {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x86.Build.0 = Debug|Win32 {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|ARM64.ActiveCfg = Release|ARM64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|ARM64.Build.0 = Release|ARM64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x64.ActiveCfg = Release|x64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x64.Build.0 = Release|x64 {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x86.ActiveCfg = Release|Win32 {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x86.Build.0 = Release|Win32 {AE53419F-A769-4548-8E15-E311904DF7DF}.ReleaseSigned|ARM64.ActiveCfg = ReleaseSigned|ARM64 {AE53419F-A769-4548-8E15-E311904DF7DF}.ReleaseSigned|ARM64.Build.0 = ReleaseSigned|ARM64 {AE53419F-A769-4548-8E15-E311904DF7DF}.ReleaseSigned|x64.ActiveCfg = ReleaseSigned|x64 {AE53419F-A769-4548-8E15-E311904DF7DF}.ReleaseSigned|x64.Build.0 = ReleaseSigned|x64 {AE53419F-A769-4548-8E15-E311904DF7DF}.ReleaseSigned|x86.ActiveCfg = ReleaseSigned|Win32 {AE53419F-A769-4548-8E15-E311904DF7DF}.ReleaseSigned|x86.Build.0 = ReleaseSigned|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E58082A6-A00C-40B6-AA8C-78702BA61918} EndGlobalSection EndGlobal ================================================ FILE: WinSpyHook/WinSpyHook.def ================================================ LIBRARY EXPORTS HookFunc3 PRIVATE HookFunc4 PRIVATE HookFunc12 PRIVATE AddHook RemoveHook ================================================ FILE: WinSpyHook/WinSpyHook.vcxproj ================================================ Debug Win32 ReleaseSigned Win32 ReleaseSigned x64 Release Win32 Debug x64 Release x64 16.0 Win32Proj {1ca31fee-1f81-4c94-9205-d908841958dd} WinSpyHook 10.0 DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode DynamicLibrary false v143 true Unicode DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode DynamicLibrary false v143 true Unicode true false false true false false Level3 true WIN32;_DEBUG;WINSPYHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpp20 Windows true false WinSpyHook.def Level3 true true true WIN32;NDEBUG;WINSPYHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpp20 MultiThreaded Windows true true true false WinSpyHook.def Level3 true true true WIN32;NDEBUG;WINSPYHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpp20 MultiThreaded Windows true true true false WinSpyHook.def Level3 true _DEBUG;WINSPYHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpp20 Windows true false WinSpyHook.def Level3 true true true NDEBUG;WINSPYHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpp20 MultiThreaded Windows true true true false WinSpyHook.def Level3 true true true NDEBUG;WINSPYHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpp20 MultiThreaded Windows true true true false WinSpyHook.def Create Create Create Create Create Create ================================================ FILE: WinSpyHook/WinSpyHook.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Source Files Source Files Source Files Source Files ================================================ FILE: WinSpyHook/dllmain.cpp ================================================ // dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" HINSTANCE g_hInstDll; BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, PVOID lpReserved) { switch (reason) { case DLL_PROCESS_ATTACH: g_hInstDll = hModule; ::DisableThreadLibraryCalls(hModule); break; } return TRUE; } ================================================ FILE: WinSpyHook/hooks.cpp ================================================ #include "pch.h" #include "hooks.h" const unsigned MaxHooks = 8; struct HookEntry { HHOOK hHook; HWND CallbackWnd; DWORD ThreadId; HWND TargetHwnd; HookOptions Options; }; #pragma data_seg(".shared") unsigned g_HookCount = 0; HookEntry g_HookMap[3][MaxHooks] {}; #pragma data_seg() #pragma comment(linker, "/section:.shared,RWS") bool IsNotify(HWND hTarget, HookEntry const& entry) { if ((entry.Options & HookOptions::Thread) == HookOptions::Thread) return true; if ((entry.Options & HookOptions::Window) == HookOptions::Window && hTarget == entry.TargetHwnd) return true; // // check if child window // return entry.TargetHwnd == ::GetAncestor(hTarget, GA_PARENT); } LRESULT WINAPI HookFunc3(int code, WPARAM wParam, LPARAM lParam) { if (code == HC_ACTION) { auto msg = reinterpret_cast(lParam); auto hWnd = msg->hwnd; auto tid = ::GetWindowThreadProcessId(hWnd, nullptr); ATLTRACE(L"HookFunc3: hWnd: 0x%p TID: %u\n", hWnd, tid); for (auto& hook : g_HookMap[0]) { if (hook.ThreadId == tid) { if (IsNotify(hWnd, hook)) { ATLTRACE(L"HookFunc3: Sending message to 0x%p (%u)\n", hook.CallbackWnd, WM_HOOKCALLBACK); COPYDATASTRUCT cds; GetMessageData data; data.Msg = *msg; data.HookType = WH_GETMESSAGE; data.wParam = wParam; cds.cbData = sizeof(data); cds.lpData = &data; ::SendMessage(hook.CallbackWnd, WM_COPYDATA, 0, reinterpret_cast(&cds)); } } } } return ::CallNextHookEx(nullptr, code, wParam, lParam); } LRESULT WINAPI HookFunc4(int code, WPARAM wParam, LPARAM lParam) { return ::CallNextHookEx(nullptr, code, wParam, lParam); } LRESULT WINAPI HookFunc12(int code, WPARAM wParam, LPARAM lParam) { return ::CallNextHookEx(nullptr, code, wParam, lParam); } bool WINAPI AddHook(DWORD hookType, HookConfig const& config) { if (g_HookCount == MaxHooks) return false; extern HINSTANCE g_hInstDll; CStringA name; name.Format("HookFunc%u", hookType); auto proc = (HOOKPROC)::GetProcAddress(g_hInstDll, name); ATLASSERT(proc); auto hHook = ::SetWindowsHookEx(hookType, proc, g_hInstDll, config.ThreadId); if (!hHook) return false; auto index = -1; switch (hookType) { case WH_GETMESSAGE: index = 0; break; case WH_CALLWNDPROC: index = 1; break; case WH_CALLWNDPROCRET: index = 2; break; default: ATLASSERT(false); break; } if (index < 0) return false; HookEntry entry{ hHook, config.CallbackWnd, config.ThreadId, config.TargetWnd, config.Options }; //std::lock_guard locker(g_Lock); g_HookMap[index][g_HookCount++] = entry; return true; } bool WINAPI RemoveHook(DWORD tid) { //std::lock_guard locker(g_Lock); for (int i = 0; i < _countof(g_HookMap); i++) { for(auto& hook : g_HookMap[i]) { if (hook.ThreadId == tid) { ::UnhookWindowsHookEx(hook.hHook); hook.ThreadId = 0; break; } } } return false; } ================================================ FILE: WinSpyHook/hooks.h ================================================ #pragma once const UINT WM_HOOKCALLBACK = WM_USER + 333; enum class HookOptions { None = 0, Window = 1, ChildWindows = 2, Thread = 4, }; DEFINE_ENUM_FLAG_OPERATORS(HookOptions); struct HookConfig { HWND TargetWnd; DWORD ThreadId; HWND CallbackWnd; HookOptions Options; }; bool WINAPI AddHook(DWORD hookType, HookConfig const& config); bool WINAPI RemoveHook(DWORD tid); struct HookDataHeader { DWORD HookType; }; struct GetMessageData : HookDataHeader { WPARAM wParam; MSG Msg; }; ================================================ FILE: WinSpyHook/pch.cpp ================================================ // pch.cpp: source file corresponding to the pre-compiled header #include "pch.h" // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. ================================================ FILE: WinSpyHook/pch.h ================================================ // pch.h: This is a precompiled header file. // Files listed below are compiled only once, improving build performance for future builds. // This also affects IntelliSense performance, including code completion and many code browsing features. // However, files listed here are ALL re-compiled if any one of them is updated between builds. // Do not add files here that you will be updating frequently as this negates the performance advantage. #ifndef PCH_H #define PCH_H #include #include #include #include #include #include #endif //PCH_H