Full Code of zodiacon/WinSpy for AI

master da6a441695fa cached
63 files
224.8 KB
67.5k tokens
193 symbols
1 requests
Download .txt
Showing preview only (240K chars total). Download the full file or copy to clipboard to get everything.
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<CAboutDlg> {
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<IUIAutomationElement> spElem;
	pWalker->GetFirstChildElement(root, &spElem);
	int pid = 0;
	while (spElem) {
		spElem->get_CurrentProcessId(&pid);
		if (pid != ::GetCurrentProcessId()) {
			auto node = AddElement(spElem, hParent, hAfter);
			CComPtr<IUIAutomationElement> 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<IUIAutomationElement> 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<IUIAutomationTreeWalker> spWalker;
	m_spUI->get_RawViewWalker(&spWalker);
	ATLASSERT(spWalker);
	m_spUIWalker = spWalker;

	CComPtr<IUIAutomationElement> 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 <TreeViewHelper.h>
#include <VirtualListView.h>
#include <CustomSplitterWindow.h>
#include <UIAutomation.h>

struct IUIAutomationElement;
struct IUIAutomationTreeWalker;

class CAutomationTreeView :
	public CViewBase<CAutomationTreeView>,
	public CVirtualListView<CAutomationTreeView>,
	public CTreeViewHelper<CAutomationTreeView> {
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<CAutomationTreeView>)
		CHAIN_MSG_MAP(CVirtualListView<CAutomationTreeView>)
		CHAIN_MSG_MAP(CViewBase<CAutomationTreeView>)
	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<IUIAutomationTreeWalker> m_spUIWalker;
	CComPtr<IUIAutomation> m_spUI;
	std::vector<ItemData> m_Properties;
};


================================================
FILE: WinSpy/DialogHelper.h
================================================
#pragma once

template<typename T>
class CDialogHelper {
public:
#ifdef IDI_OK
	void AdjustOKCancelButtons() {
		auto dlg = static_cast<T*>(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<T*>(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<T*>(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<T*>(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<CFindWindowDlg>,
	public CDynamicDialogLayout<CFindWindowDlg>,
	public CDialogHelper<CFindWindowDlg> {
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<CFindWindowDlg>)
	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<CStatic> 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 <sstream>

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<typename T>
struct CFrameWindowHelper : CIdleHandler {
	HWND CreateAndInitToolBar(const ToolBarButtonInfo* buttons, int count) {
		auto pT = static_cast<T*>(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<T*>(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<std::wstring, int>;
	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<CMainFrame>::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<LPARAM>(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 <CustomTabView.h>
#include <OwnerDrawnMenu.h>

class CMainFrame : 
	public CFrameWindowImpl<CMainFrame>, 
	public CAutoUpdateUI<CMainFrame>,
	public COwnerDrawnMenu<CMainFrame>,
	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<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
		COMMAND_RANGE_HANDLER(1, 0xffff, OnCommandToActiveView)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabActivated)
		CHAIN_MSG_MAP(COwnerDrawnMenu<CMainFrame>)
	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<GetMessageData*>(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<ColumnType>(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<COPYDATASTRUCT*>(lp);
	auto header = reinterpret_cast<HookDataHeader*>(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> {
	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<CMessagesView>, CVirtualListView<CMessagesView> {
	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<CMessagesView>)
		CHAIN_MSG_MAP(CViewBase<CMessagesView>)
	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<MessageInfo> m_Messages;
	std::vector<MessageInfo> 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 <TlHelp32.h>
#include <unordered_set>

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<DWORD, int> 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<DWORD> 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<DWORD> 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<ProcessInfo> Processes;
	std::unordered_map<DWORD, std::vector<HWND>> 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<DWORD, CString> _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<LPARAM>(&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<LPARAM>(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<ItemType>(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<ItemType>(data)) {
		case ItemType::Process:
			ProcessHelper::ShowProcessProperties(m_Processes[hItem]);
			break;

		case ItemType::Thread:
			break;

		default:
			auto hWnd = reinterpret_cast<HWND>(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<ItemType>(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<CProcessesView>,
	public CTreeViewManager<CProcessesView> {
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<CProcessesView>)
		CHAIN_MSG_MAP(CViewBase<CProcessesView>)
		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<HTREEITEM, ProcessInfo> m_Processes;
	std::unordered_map<HTREEITEM, DWORD> m_Threads;
	std::unordered_map<HWND, HTREEITEM> 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<typename T>
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<T*>(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<T*>(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<typename T, typename TBase = CFrameWindowImpl<T, CWindow, CControlWinTraits>>
class CViewBase abstract :
	public TBase,
	public CAutoUpdateUI<T>,
	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<T>::UIUpdateToolBar();
		return FALSE;
	}

	LRESULT OnTabActivated(UINT /*uMsg*/, WPARAM wParam, LPARAM, BOOL& bHandled) {
		auto pT = static_cast<T*>(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<T*>(this);

		bHandled = FALSE;
		if (pT->m_hWndToolBar)
			_Module.GetMessageLoop()->RemoveIdleHandler(this);
		return 0;
	}

	HWND CreateAndInitToolBar(const ToolBarButtonInfo* buttons, int count) {
		auto pT = static_cast<T*>(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 <ThemeHelper.h>

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         "<a>SysLink1</a>",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         "<a>SysLink1</a>",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         "<a>SysLink1</a>",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         "<a>SysLink1</a>",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
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="ReleaseSigned|Win32">
      <Configuration>ReleaseSigned</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="ReleaseSigned|x64">
      <Configuration>ReleaseSigned</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <ProjectGuid>{3988E48D-F582-4E0F-B1AF-FC70A4D0B4D7}</ProjectGuid>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|Win32'" Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|x64'" Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
    <IncludePath>..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <IncludePath>..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
    <IncludePath>..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|Win32'">
    <LinkIncremental>false</LinkIncremental>
    <IncludePath>..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
    <IncludePath>..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|x64'">
    <LinkIncremental>false</LinkIncremental>
    <IncludePath>..\WTL;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
      <Optimization>Disabled</Optimization>
      <PreprocessorDefinitions>WIN32;_WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <ExceptionHandling>false</ExceptionHandling>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <AdditionalIncludeDirectories>..\WinSpyHook;..\WTLHelper\WTLHelper</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
    <ResourceCompile>
      <Culture>0x0409</Culture>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <MkTypLibCompatible>false</MkTypLibCompatible>
      <TargetEnvironment>Win32</TargetEnvironment>
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <HeaderFileName>WinSpy.h</HeaderFileName>
      <InterfaceIdentifierFileName>WinSpy_i.c</InterfaceIdentifierFileName>
      <ProxyFileName>WinSpy_p.c</ProxyFileName>
      <GenerateStublessProxies>true</GenerateStublessProxies>
      <TypeLibraryName>$(IntDir)/WinSpy.tlb</TypeLibraryName>
      <DllDataFileName />
    </Midl>
    <Manifest>
      <EnableDpiAwareness>false</EnableDpiAwareness>
    </Manifest>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
      <Optimization>Disabled</Optimization>
      <PreprocessorDefinitions>_WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <ExceptionHandling>false</ExceptionHandling>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <AdditionalIncludeDirectories>..\WinSpyHook;..\WTLHelper\WTLHelper</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
    <ResourceCompile>
      <Culture>0x0409</Culture>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <MkTypLibCompatible>false</MkTypLibCompatible>
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <HeaderFileName>WinSpy.h</HeaderFileName>
      <InterfaceIdentifierFileName>WinSpy_i.c</InterfaceIdentifierFileName>
      <ProxyFileName>WinSpy_p.c</ProxyFileName>
      <GenerateStublessProxies>true</GenerateStublessProxies>
      <TypeLibraryName>$(IntDir)/WinSpy.tlb</TypeLibraryName>
      <DllDataFileName />
    </Midl>
    <Manifest>
      <EnableDpiAwareness>false</EnableDpiAwareness>
    </Manifest>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <ExceptionHandling>false</ExceptionHandling>
      <DebugInformationFormat />
      <PreprocessorDefinitions>WIN32;_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <AdditionalIncludeDirectories>..\WinSpyHook;..\WTLHelper\WTLHelper</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
    </Link>
    <ResourceCompile>
      <Culture>0x0409</Culture>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <MkTypLibCompatible>false</MkTypLibCompatible>
      <TargetEnvironment>Win32</TargetEnvironment>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <HeaderFileName>WinSpy.h</HeaderFileName>
      <InterfaceIdentifierFileName>WinSpy_i.c</InterfaceIdentifierFileName>
      <ProxyFileName>WinSpy_p.c</ProxyFileName>
      <GenerateStublessProxies>true</GenerateStublessProxies>
      <TypeLibraryName>$(IntDir)/WinSpy.tlb</TypeLibraryName>
      <DllDataFileName />
    </Midl>
    <PostBuildEvent>
      <Command>
      </Command>
    </PostBuildEvent>
    <Manifest>
      <EnableDpiAwareness>false</EnableDpiAwareness>
    </Manifest>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <ExceptionHandling>false</ExceptionHandling>
      <DebugInformationFormat>
      </DebugInformationFormat>
      <PreprocessorDefinitions>WIN32;_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <AdditionalIncludeDirectories>..\WinSpyHook;..\WTLHelper\WTLHelper</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
    </Link>
    <ResourceCompile>
      <Culture>0x0409</Culture>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <MkTypLibCompatible>false</MkTypLibCompatible>
      <TargetEnvironment>Win32</TargetEnvironment>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <HeaderFileName>WinSpy.h</HeaderFileName>
      <InterfaceIdentifierFileName>WinSpy_i.c</InterfaceIdentifierFileName>
      <ProxyFileName>WinSpy_p.c</ProxyFileName>
      <GenerateStublessProxies>true</GenerateStublessProxies>
      <TypeLibraryName>$(IntDir)/WinSpy.tlb</TypeLibraryName>
      <DllDataFileName>
      </DllDataFileName>
    </Midl>
    <PostBuildEvent>
      <Command>signtool sign /i DigiCert /fd sha256 $(TargetPath)</Command>
    </PostBuildEvent>
    <Manifest>
      <EnableDpiAwareness>false</EnableDpiAwareness>
    </Manifest>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <ExceptionHandling>false</ExceptionHandling>
      <DebugInformationFormat />
      <PreprocessorDefinitions>_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <AdditionalIncludeDirectories>..\WinSpyHook;..\WTLHelper\WTLHelper</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
    </Link>
    <ResourceCompile>
      <Culture>0x0409</Culture>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <MkTypLibCompatible>false</MkTypLibCompatible>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <HeaderFileName>WinSpy.h</HeaderFileName>
      <InterfaceIdentifierFileName>WinSpy_i.c</InterfaceIdentifierFileName>
      <ProxyFileName>WinSpy_p.c</ProxyFileName>
      <GenerateStublessProxies>true</GenerateStublessProxies>
      <TypeLibraryName>$(IntDir)/WinSpy.tlb</TypeLibraryName>
      <DllDataFileName />
    </Midl>
    <PostBuildEvent>
      <Command>
      </Command>
    </PostBuildEvent>
    <Manifest>
      <EnableDpiAwareness>false</EnableDpiAwareness>
    </Manifest>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|x64'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <ExceptionHandling>false</ExceptionHandling>
      <DebugInformationFormat>
      </DebugInformationFormat>
      <PreprocessorDefinitions>_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <AdditionalIncludeDirectories>..\WinSpyHook;..\WTLHelper\WTLHelper</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
    </Link>
    <ResourceCompile>
      <Culture>0x0409</Culture>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <MkTypLibCompatible>false</MkTypLibCompatible>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <HeaderFileName>WinSpy.h</HeaderFileName>
      <InterfaceIdentifierFileName>WinSpy_i.c</InterfaceIdentifierFileName>
      <ProxyFileName>WinSpy_p.c</ProxyFileName>
      <GenerateStublessProxies>true</GenerateStublessProxies>
      <TypeLibraryName>$(IntDir)/WinSpy.tlb</TypeLibraryName>
      <DllDataFileName>
      </DllDataFileName>
    </Midl>
    <PostBuildEvent>
      <Command>signtool sign /a /n Scorpio /t http://timestamp.digicert.com /fd sha256 $(TargetPath)</Command>
    </PostBuildEvent>
    <Manifest>
      <EnableDpiAwareness>false</EnableDpiAwareness>
    </Manifest>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="AboutDlg.cpp" />
    <ClCompile Include="AutomationTreeView.cpp" />
    <ClCompile Include="HookHelper.cpp" />
    <ClCompile Include="IconHelper.cpp" />
    <ClCompile Include="ImageIconCache.cpp" />
    <ClCompile Include="MessageDecoder.cpp" />
    <ClCompile Include="MessagesView.cpp" />
    <ClCompile Include="ProcessesView.cpp" />
    <ClCompile Include="SecurityHelper.cpp" />
    <ClCompile Include="WindowGeneralPage.cpp" />
    <ClCompile Include="WindowsListView.cpp" />
    <ClCompile Include="FindWindowDlg.cpp" />
    <ClCompile Include="FormatHelper.cpp" />
    <ClCompile Include="MainFrm.cpp" />
    <ClCompile Include="pch.cpp">
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseSigned|x64'">Create</PrecompiledHeader>
    </ClCompile>
    <ClCompile Include="ProcessHelper.cpp" />
    <ClCompile Include="WindowHelper.cpp" />
    <ClCompile Include="WindowsView.cpp" />
    <ClCompile Include="WindowWindowsPage.cpp" />
    <ClCompile Include="WinSpy.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="AboutDlg.h" />
    <ClInclude Include="AutomationTreeView.h" />
    <ClInclude Include="DialogHelper.h" />
    <ClInclude Include="HookHelper.h" />
    <ClInclude Include="IconHelper.h" />
    <ClInclude Include="ImageIconCache.h" />
    <ClInclude Include="MessageDecoder.h" />
    <ClInclude Include="MessagesView.h" />
    <ClInclude Include="SecurityHelper.h" />
    <ClInclude Include="FrameWindowHelper.h" />
    <ClInclude Include="WindowGeneralPage.h" />
    <ClInclude Include="WindowsListView.h" />
    <ClInclude Include="FindWindowDlg.h" />
    <ClInclude Include="FormatHelper.h" />
    <ClInclude Include="Interfaces.h" />
    <ClInclude Include="MainFrm.h" />
    <ClInclude Include="ProcessesView.h" />
    <ClInclude Include="ProcessHelper.h" />
    <ClInclude Include="resource.h" />
    <ClInclude Include="pch.h" />
    <ClInclude Include="TreeViewManager.h" />
    <ClInclude Include="ViewBase.h" />
    <ClInclude Include="WindowHelper.h" />
    <ClInclude Include="WindowsView.h" />
    <ClInclude Include="WindowWindowsPage.h" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="WinSpy.rc" />
  </ItemGroup>
  <ItemGroup>
    <Image Include="res\Automation.ico" />
    <Image Include="res\close-window.ico" />
    <Image Include="res\gears.ico" />
    <Image Include="res\gear_information.ico" />
    <Image Include="res\maximize.ico" />
    <Image Include="res\message.ico" />
    <Image Include="res\messages.ico" />
    <Image Include="res\Minimize.ico" />
    <Image Include="res\refresh.ico" />
    <Image Include="res\restore.ico" />
    <Image Include="res\Target.ico" />
    <Image Include="res\window-close.ico" />
    <Image Include="res\window-hidden.ico" />
    <Image Include="res\window-notext.ico" />
    <Image Include="res\window-properties.ico" />
    <Image Include="res\window.ico" />
    <Image Include="res\windows.ico" />
    <Image Include="res\WindowSearch.ico" />
    <Image Include="res\WindowSendToBack.ico" />
    <Image Include="res\WindowSendToFront.ico" />
    <Image Include="res\window_delete.ico" />
    <Image Include="res\window_new.ico" />
    <Image Include="res\WinSpy.ico" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\WTLHelper\WTLHelper\WTLHelper.vcxproj">
      <Project>{ae53419f-a769-4548-8e15-e311904df7df}</Project>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>

================================================
FILE: WinSpy/WinSpy.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Filter Include="Source Files">
      <UniqueIdentifier>{f871bb5c-f151-4e10-8bbb-10019ddd6a72}</UniqueIdentifier>
      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm</Extensions>
    </Filter>
    <Filter Include="Header Files">
      <UniqueIdentifier>{47fb17d7-d54c-4fc7-85b2-7c653d04b30a}</UniqueIdentifier>
      <Extensions>h;hpp;hxx;hm;inl;inc</Extensions>
    </Filter>
    <Filter Include="Resource Files">
      <UniqueIdentifier>{9c7daf0d-2916-49f7-872d-30cf5e95725d}</UniqueIdentifier>
      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest</Extensions>
    </Filter>
    <Filter Include="Resource Files\Icons">
      <UniqueIdentifier>{ff137a25-faa3-4d88-af38-5c1bc296546b}</UniqueIdentifier>
    </Filter>
    <Filter Include="Dialogs">
      <UniqueIdentifier>{96be1941-f83d-4366-ad55-930da999a2eb}</UniqueIdentifier>
    </Filter>
    <Filter Include="Views">
      <UniqueIdentifier>{9a542756-dfdb-4a47-b231-2b2ae2aef120}</UniqueIdentifier>
    </Filter>
    <Filter Include="Helpers">
      <UniqueIdentifier>{3b473b33-73a4-4bd6-a469-d86318a53f81}</UniqueIdentifier>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="WinSpy.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="MainFrm.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="AboutDlg.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="pch.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="FindWindowDlg.cpp">
      <Filter>Dialogs</Filter>
    </ClCompile>
    <ClCompile Include="WindowsView.cpp">
      <Filter>Views</Filter>
    </ClCompile>
    <ClCompile Include="ProcessHelper.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="FormatHelper.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="WindowHelper.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="WindowsListView.cpp">
      <Filter>Views</Filter>
    </ClCompile>
    <ClCompile Include="IconHelper.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="ProcessesView.cpp">
      <Filter>Views</Filter>
    </ClCompile>
    <ClCompile Include="ImageIconCache.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="WindowGeneralPage.cpp">
      <Filter>Dialogs</Filter>
    </ClCompile>
    <ClCompile Include="WindowWindowsPage.cpp">
      <Filter>Dialogs</Filter>
    </ClCompile>
    <ClCompile Include="SecurityHelper.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="MessagesView.cpp">
      <Filter>Views</Filter>
    </ClCompile>
    <ClCompile Include="MessageDecoder.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="HookHelper.cpp">
      <Filter>Helpers</Filter>
    </ClCompile>
    <ClCompile Include="AutomationTreeView.cpp">
      <Filter>Views</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="MainFrm.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="AboutDlg.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="pch.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="ViewBase.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="Interfaces.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="TreeViewManager.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="FindWindowDlg.h">
      <Filter>Dialogs</Filter>
    </ClInclude>
    <ClInclude Include="WindowsView.h">
      <Filter>Views</Filter>
    </ClInclude>
    <ClInclude Include="ProcessesView.h">
      <Filter>Views</Filter>
    </ClInclude>
    <ClInclude Include="WindowHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="ProcessHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="resource.h">
      <Filter>Resource Files</Filter>
    </ClInclude>
    <ClInclude Include="FormatHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="WindowsListView.h">
      <Filter>Views</Filter>
    </ClInclude>
    <ClInclude Include="IconHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="ImageIconCache.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="WindowGeneralPage.h">
      <Filter>Dialogs</Filter>
    </ClInclude>
    <ClInclude Include="DialogHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="WindowWindowsPage.h">
      <Filter>Dialogs</Filter>
    </ClInclude>
    <ClInclude Include="SecurityHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="MessagesView.h">
      <Filter>Views</Filter>
    </ClInclude>
    <ClInclude Include="FrameWindowHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="MessageDecoder.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="HookHelper.h">
      <Filter>Helpers</Filter>
    </ClInclude>
    <ClInclude Include="AutomationTreeView.h">
      <Filter>Views</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="WinSpy.rc">
      <Filter>Resource Files</Filter>
    </ResourceCompile>
  </ItemGroup>
  <ItemGroup>
    <Image Include="res\window.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\window-hidden.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\WinSpy.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\window_delete.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\window_new.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\windows.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\refresh.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\window-close.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\window-notext.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\WindowSendToBack.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\Minimize.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\maximize.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\close-window.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\Target.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\window-properties.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\gears.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\gear_information.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\message.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\messages.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\WindowSearch.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\WindowSendToFront.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\restore.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
    <Image Include="res\Automation.ico">
      <Filter>Resource Files\Icons</Filter>
    </Image>
  </ItemGroup>
</Project>

================================================
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<StyleItem const*, int> 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<StyleItem const*, int> 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<HWND>(lp));
	UpdateData();

	return 0;
}


================================================
FILE: WinSpy/WindowGeneralPage.h
================================================
#pragma once

#include "DialogHelper.h"

struct StyleItem;

class CWindowGeneralPage :
	public CDialogHelper<CWindowGeneralPage>,
	public CPropertyPageImpl<CWindowGeneralPage> {
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<StyleItem const*, int> 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<HWND, int>& WindowHelper::GetIconMap() {
	static std::unordered_map<HWND, int> 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<bool*>(param) = true;
		return FALSE;
		}, reinterpret_cast<LPARAM>(&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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> WindowHelper::GetStatusBarStyleArray() {
	static const StyleItem styles[] = {
		PAIR_STR(SBARS_SIZEGRIP),
		PAIR_STR(SBARS_TOOLTIPS),
	};
	return std::make_pair(styles, (int)_countof(styles));
}

std::pair<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<StyleItem const*, int> 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<HWND, int>& 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<StyleItem const*, int> GetWindowStyleArray();
	static std::pair<StyleItem const*, int> GetListViewStyleArray();
	static std::pair<StyleItem const*, int> GetTreeViewStyleArray();
	static std::pair<StyleItem const*, int> GetTabCtrlStyleArray();
	static std::pair<StyleItem const*, int> GetWindowStyleExArray();
	static std::pair<StyleItem const*, int> GetClassStyleArray();
	static std::pair<StyleItem const*, int> GetEditStyleArray();
	static std::pair<StyleItem const*, int> GetListBoxStyleArray();
	static std::pair<StyleItem const*, int> GetComboBoxStyleArray();
	static std::pair<StyleItem const*, int> GetHeaderStyleArray();
	static std::pair<StyleItem const*, int> GetButtonStyleArray();
	static std::pair<StyleItem const*, int> GetStaticStyleArray();
	static std::pair<StyleItem const*, int> GetToolTipStyleArray();
	static std::pair<StyleItem const*, int> GetStatusBarStyleArray();
	static std::pair<StyleItem const*, int> GetToolBarStyleArray();
	static std::pair<StyleItem const*, int> GetRebarStyleArray();
	static std::pair<StyleItem const*, int> 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"<a>0x%zX</a>", (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<LPARAM>(hWnd));
    return 0;
}



================================================
FILE: WinSpy/WindowWindowsPage.h
================================================
#pragma once

#include "DialogHelper.h"

class CWindowWindowsPage :
	public CDialogHelper<CWindowWindowsPage>,
	public CPropertyPageImpl<CWindowWindowsPage> {
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<DataItemType>(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<DataItemType>(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<CWindowsListView*>(param);
		p->m_Items.push_back(WindowHelper::GetWindowInfo(hWnd));
		return TRUE;
		}, reinterpret_cast<LPARAM>(this));
}

void CWindowsListView::Refresh() {
}

void CWindowsListView::AddChildWindows(std::vector<WindowItem>& v, HWND hParent, bool directOnly) {
	struct LocalInfo {
		std::vector<WindowItem>& v;
		CWindowsListView* pThis;
		bool directOnly;
	};

	LocalInfo info{ v, this, directOnly };

	::EnumChildWindows(hParent, [](auto hWnd, auto param) {
		auto info = reinterpret_cast<LocalInfo*>(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<LPARAM>(&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 <VirtualListView.h>
#include "Interfaces.h"
#include "WindowHelper.h"
#include "ProcessHelper.h"

class CWindowsListView : 
	public CFrameWindowImpl<CWindowsListView, CWindow, CControlWinTraits>,
	public CVirtualListView<CWindowsListView>,
	public CCustomDraw<CWindowsListView> {
public:
	using BaseFrame = CFrameWindowImpl<CWindowsListView, CWindow, CControlWinTraits>;

	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<CWindowsListView>)
		CHAIN_MSG_MAP(CCustomDraw<CWindowsListView>)
		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<WindowItem>& 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<WindowItem> 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<LPARAM>(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<LPARAM>(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
================================================
Download .txt
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
Download .txt
SYMBOL INDEX (193 symbols across 38 files)

FILE: WinSpy/AboutDlg.cpp
  function LRESULT (line 10) | LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM...
  function LRESULT (line 15) | LRESULT CAboutDlg::OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWn...

FILE: WinSpy/AutomationTreeView.cpp
  function LRESULT (line 7) | LRESULT CAutomationTreeView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, ...
  function LRESULT (line 31) | LRESULT CAutomationTreeView::OnNodeExpanding(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 48) | LRESULT CAutomationTreeView::OnNodeExpanded(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 70) | LRESULT CAutomationTreeView::OnNodeSelected(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 78) | LRESULT CAutomationTreeView::OnNodeDeleted(int, LPNMHDR hdr, BOOL&) {
  function CString (line 93) | CString CAutomationTreeView::GetColumnText(HWND, int row, int col) const {
  function HTREEITEM (line 102) | HTREEITEM CAutomationTreeView::AddElement(IUIAutomationElement* e, HTREE...

FILE: WinSpy/AutomationTreeView.h
  type IUIAutomationElement (line 9) | struct IUIAutomationElement
  type IUIAutomationTreeWalker (line 10) | struct IUIAutomationTreeWalker

FILE: WinSpy/DialogHelper.h
  function AdjustOKCancelButtons (line 7) | void AdjustOKCancelButtons() {
  function SetDialogIcon (line 37) | void SetDialogIcon(UINT icon) {
  function SetDialogIcon (line 42) | void SetDialogIcon(HICON icon) {

FILE: WinSpy/FindWindowDlg.cpp
  function HWND (line 8) | HWND CFindWindowDlg::GetSelectedHwnd() const {
  function LRESULT (line 44) | LRESULT CFindWindowDlg::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 54) | LRESULT CFindWindowDlg::OnCloseCmd(WORD, WORD id, HWND, BOOL&) {
  function LRESULT (line 71) | LRESULT CFindWindowDlg::OnSearch(WORD, WORD id, HWND, BOOL&) {
  function LRESULT (line 100) | LRESULT CFindWindowDlg::OnMouseDown(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 110) | LRESULT CFindWindowDlg::OnMouseUp(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 123) | LRESULT CFindWindowDlg::OnMouseMove(UINT, WPARAM, LPARAM lp, BOOL&) {

FILE: WinSpy/FindWindowDlg.h
  function LRESULT (line 39) | LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/...

FILE: WinSpy/FormatHelper.cpp
  function CString (line 5) | CString FormatHelper::FormatHWndOrNone(HWND hWnd) {
  function CString (line 14) | CString FormatHelper::RectToString(CRect const& rc) {
  function DWORD_PTR (line 20) | DWORD_PTR FormatHelper::ParseHex(CString const& text) {
  function CString (line 32) | CString FormatHelper::FormatPoint(POINT const& pt) {

FILE: WinSpy/FormatHelper.h
  type FormatHelper (line 3) | struct FormatHelper {

FILE: WinSpy/FrameWindowHelper.h
  function HWND (line 7) | HWND CreateAndInitToolBar(const ToolBarButtonInfo* buttons, int count) {
  function BOOL (line 36) | BOOL OnIdle() override {

FILE: WinSpy/HookHelper.h
  function abstract (line 5) | struct HookHelper abstract final {

FILE: WinSpy/IconHelper.cpp
  function HICON (line 4) | HICON IconHelper::GetShieldIcon() {
  function HICON (line 8) | HICON IconHelper::GetStockIcon(SHSTOCKICONID id, bool big) {

FILE: WinSpy/IconHelper.h
  type IconHelper (line 3) | struct IconHelper {

FILE: WinSpy/ImageIconCache.cpp
  function HIMAGELIST (line 49) | HIMAGELIST ImageIconCache::GetImageList() const {
  function ImageIconCache (line 53) | ImageIconCache& ImageIconCache::Get() {

FILE: WinSpy/ImageIconCache.h
  type ImageIconCache (line 3) | struct ImageIconCache {

FILE: WinSpy/Interfaces.h
  type CMessagesView (line 3) | struct CMessagesView
  type IMainFrame (line 5) | struct IMainFrame {
  type ToolBarButtonInfo (line 13) | struct ToolBarButtonInfo {

FILE: WinSpy/MainFrm.cpp
  function BOOL (line 20) | BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) {
  function BOOL (line 27) | BOOL CMainFrame::OnIdle() {
  function LRESULT (line 32) | LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*...
  function LRESULT (line 83) | LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /...
  function LRESULT (line 94) | LRESULT CMainFrame::OnMenuSelect(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 98) | LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND ...
  function LRESULT (line 103) | LRESULT CMainFrame::OnViewAllWindows(WORD /*wNotifyCode*/, WORD /*wID*/,...
  function LRESULT (line 115) | LRESULT CMainFrame::OnViewAllProcesses(WORD /*wNotifyCode*/, WORD /*wID*...
  function LRESULT (line 127) | LRESULT CMainFrame::OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HW...
  function LRESULT (line 138) | LRESULT CMainFrame::OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, ...
  function LRESULT (line 146) | LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND ...
  function LRESULT (line 152) | LRESULT CMainFrame::OnWindowClose(WORD /*wNotifyCode*/, WORD /*wID*/, HW...
  function LRESULT (line 162) | LRESULT CMainFrame::OnWindowCloseAll(WORD /*wNotifyCode*/, WORD /*wID*/,...
  function LRESULT (line 168) | LRESULT CMainFrame::OnWindowActivate(WORD /*wNotifyCode*/, WORD wID, HWN...
  function LRESULT (line 175) | LRESULT CMainFrame::OnCommandToActiveView(WORD code, WORD id, HWND h, BO...
  function LRESULT (line 183) | LRESULT CMainFrame::OnFindWindow(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 192) | LRESULT CMainFrame::OnRunAsAdmin(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 199) | LRESULT CMainFrame::OnTabCloseButton(int, LPNMHDR, BOOL&) {
  function LRESULT (line 203) | LRESULT CMainFrame::OnViewAutomationTree(WORD, WORD, HWND, BOOL&) {
  function CUpdateUIBase (line 211) | CUpdateUIBase& CMainFrame::GetUIUpdate() {
  function UINT (line 215) | UINT CMainFrame::ShowPopupMenu(HMENU hMenu, const POINT& pt, DWORD flags) {
  function CMessagesView (line 219) | CMessagesView* CMainFrame::CreateMessagesView() {
  function LRESULT (line 295) | LRESULT CMainFrame::OnTabActivated(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOO...

FILE: WinSpy/MessageDecoder.cpp
  function CString (line 7) | CString MessageDecoder::Decode(UINT msg, WPARAM wp, LPARAM lp) {
  function CString (line 43) | CString MessageDecoder::MouseKey(int key) {
  function CString (line 70) | CString MessageDecoder::SysCommandToString(DWORD cmd) {
  function CString (line 97) | CString MessageDecoder::SizeParamToString(DWORD type) {

FILE: WinSpy/MessageDecoder.h
  function abstract (line 3) | struct MessageDecoder final abstract {

FILE: WinSpy/MessagesView.cpp
  function CString (line 57) | CString CMessagesView::GetColumnText(HWND hWnd, int row, int col) const {
  function DWORD (line 102) | DWORD CMessagesView::ProcessHook() {
  function LRESULT (line 116) | LRESULT CMessagesView::OnCreate(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 153) | LRESULT CMessagesView::OnTimer(UINT, WPARAM id, LPARAM, BOOL&) {
  function LRESULT (line 167) | LRESULT CMessagesView::OnDestroy(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 179) | LRESULT CHookCallbackWnd::OnHookCallback(UINT msg, WPARAM wp, LPARAM lp,...

FILE: WinSpy/MessagesView.h
  type CMessagesView (line 7) | struct CMessagesView
  type CHookCallbackWnd (line 9) | struct CHookCallbackWnd
  function m_pView (line 10) | m_pView(pView) {}
  function MESSAGE_HANDLER (line 12) | BEGIN_MSG_MAP(CHookCallbackWnd)

FILE: WinSpy/ProcessHelper.cpp
  function CString (line 7) | CString ProcessHelper::GetProcessImageName(DWORD pid, bool fullPath) {
  function ProcessesInfo (line 27) | ProcessesInfo ProcessHelper::EnumProcessesAndThreads(EnumProcessesOption...

FILE: WinSpy/ProcessHelper.h
  type ProcessInfo (line 3) | struct ProcessInfo {
  function EnumProcessesOptions (line 10) | enum class EnumProcessesOptions {

FILE: WinSpy/ProcessesView.cpp
  function LRESULT (line 6) | LRESULT CProcessesView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARA...
  function LRESULT (line 33) | LRESULT CProcessesView::OnTimer(UINT /*uMsg*/, WPARAM id, LPARAM /*lPara...
  type LocalData (line 66) | struct LocalData {
  function CTreeItem (line 90) | CTreeItem CProcessesView::AddNode(HWND hWnd, HTREEITEM hParent) {
  function LRESULT (line 142) | LRESULT CProcessesView::OnNodeExpanding(int, LPNMHDR hdr, BOOL&) {
  function BOOL (line 167) | BOOL CProcessesView::AddChildNode(HWND hChild) {
  function LRESULT (line 174) | LRESULT CProcessesView::OnTreeNodeRightClick(HTREEITEM hItem, CPoint con...
  function LRESULT (line 189) | LRESULT CProcessesView::OnWindowShow(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 196) | LRESULT CProcessesView::OnWindowHide(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 202) | LRESULT CProcessesView::OnWindowMinimize(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 208) | LRESULT CProcessesView::OnWindowMaximize(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 215) | LRESULT CProcessesView::OnWindowRestore(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 221) | LRESULT CProcessesView::OnWindowFlash(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 228) | LRESULT CProcessesView::OnWindowBringToFront(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 234) | LRESULT CProcessesView::OnNodeSelected(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 246) | LRESULT CProcessesView::OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint co...
  function LRESULT (line 264) | LRESULT CProcessesView::OnWindowProperties(WORD /*wNotifyCode*/, WORD /*...
  function LRESULT (line 270) | LRESULT CProcessesView::OnProcessProperties(WORD /*wNotifyCode*/, WORD /...
  function LRESULT (line 301) | LRESULT CProcessesView::OnToggleHiddenWindows(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 308) | LRESULT CProcessesView::OnToggleEmptyTitleWindows(WORD, WORD, HWND, BOOL...

FILE: WinSpy/SecurityHelper.h
  function abstract (line 3) | struct SecurityHelper abstract final {

FILE: WinSpy/TreeViewManager.h
  function LRESULT (line 31) | LRESULT OnDoubleClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) {
  function LRESULT (line 52) | LRESULT OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint const& pt) {

FILE: WinSpy/ViewBase.h
  function OnFinalMessage (line 24) | void OnFinalMessage(HWND) override {
  function BOOL (line 28) | BOOL OnIdle() override {
  function LRESULT (line 33) | LRESULT OnTabActivated(UINT /*uMsg*/, WPARAM wParam, LPARAM, BOOL& bHand...
  function OnActivate (line 39) | void OnActivate(bool activate) {}
  function LRESULT (line 41) | LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL&...
  function HWND (line 50) | HWND CreateAndInitToolBar(const ToolBarButtonInfo* buttons, int count) {

FILE: WinSpy/WinSpy.cpp
  function Run (line 11) | int Run(LPCTSTR /*lpstrCmdLine*/ = nullptr, int nCmdShow = SW_SHOWDEFAUL...
  function _tWinMain (line 30) | int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, L...

FILE: WinSpy/WindowGeneralPage.cpp
  function LRESULT (line 111) | LRESULT CWindowGeneralPage::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 117) | LRESULT CWindowGeneralPage::OnUpdate(UINT, WPARAM, LPARAM lp, BOOL&) {

FILE: WinSpy/WindowGeneralPage.h
  type StyleItem (line 5) | struct StyleItem
  function m_Win (line 15) | CWindowGeneralPage(HWND hWnd) : m_Win(hWnd) {

FILE: WinSpy/WindowHelper.cpp
  function CString (line 13) | CString WindowHelper::WindowStyleToString(HWND hWnd) {
  function CString (line 28) | CString WindowHelper::ClassStyleToString(HWND hWnd) {
  function CString (line 44) | CString WindowHelper::WindowExtendedStyleToString(HWND hWnd) {
  function CString (line 61) | CString WindowHelper::WindowRectToString(HWND hWnd) {
  function CString (line 68) | CString WindowHelper::GetWindowClassName(HWND hWnd) {
  function CString (line 74) | CString WindowHelper::GetWindowText(HWND hWnd) {
  function HICON (line 80) | HICON WindowHelper::GetWindowOrProcessIcon(HWND hWnd) {
  function HICON (line 126) | HICON WindowHelper::GetWindowIcon(HWND hWnd) {
  function CString (line 135) | CString WindowHelper::GetWindowClassAndTitle(HWND hWnd) {
  function CImageList (line 155) | CImageList& WindowHelper::GetImageList() {
  function WindowItem (line 164) | WindowItem WindowHelper::GetWindowInfo(HWND hWnd) {
  function CString (line 537) | CString WindowHelper::WindowMessageToString(DWORD msg) {
  function ULONG_PTR (line 785) | ULONG_PTR WindowHelper::GetID(HWND hWnd) {
  function ULONG_PTR (line 789) | ULONG_PTR WindowHelper::GetUserData(HWND hWnd) {

FILE: WinSpy/WindowHelper.h
  type WindowItem (line 3) | struct WindowItem {
  type StyleItem (line 10) | struct StyleItem {
  function abstract (line 16) | struct WindowHelper abstract final {

FILE: WinSpy/WindowWindowsPage.cpp
  function LRESULT (line 34) | LRESULT CWindowWindowsPage::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 40) | LRESULT CWindowWindowsPage::OnClickHandle(int, LPNMHDR hdr, BOOL&) {

FILE: WinSpy/WindowsListView.cpp
  function LRESULT (line 13) | LRESULT CWindowsListView::OnCreate(UINT, WPARAM, LPARAM, BOOL&) {
  function LRESULT (line 61) | LRESULT CWindowsListView::OnWindowShow(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 68) | LRESULT CWindowsListView::OnWindowHide(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 74) | LRESULT CWindowsListView::OnWindowMinimize(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 80) | LRESULT CWindowsListView::OnWindowMaximize(WORD, WORD, HWND, BOOL&) {
  function CString (line 87) | CString CWindowsListView::GetColumnText(HWND, int row, int col) const {
  function DWORD (line 229) | DWORD CWindowsListView::OnPrePaint(int, LPNMCUSTOMDRAW cd) {
  function DWORD (line 236) | DWORD CWindowsListView::OnItemPrePaint(int, LPNMCUSTOMDRAW cd) {
  type LocalInfo (line 316) | struct LocalInfo {
  function CString (line 335) | CString CWindowsListView::GetDetails(const DataItem& item) const {
  function LRESULT (line 373) | LRESULT CWindowsListView::OnToggleEmptyTitleWindows(WORD, WORD, HWND, BO...
  function LRESULT (line 380) | LRESULT CWindowsListView::OnToggleChildWindows(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 387) | LRESULT CWindowsListView::OnWindowRestore(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 393) | LRESULT CWindowsListView::OnWindowBringToFront(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 399) | LRESULT CWindowsListView::OnWindowProperties(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 405) | LRESULT CWindowsListView::OnItemChanged(int, LPNMHDR, BOOL&) {
  function LRESULT (line 410) | LRESULT CWindowsListView::OnWindowFlash(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 417) | LRESULT CWindowsListView::OnToggleHiddenWindows(WORD, WORD, HWND, BOOL&) {

FILE: WinSpy/WindowsListView.h
  function m_pFrame (line 15) | m_pFrame(frame) {}
  type class (line 35) | enum class
  type DataItem (line 41) | struct DataItem {

FILE: WinSpy/WindowsView.cpp
  function BOOL (line 15) | BOOL CWindowsView::PreTranslateMessage(MSG* pMsg) {
  function LRESULT (line 51) | LRESULT CWindowsView::OnRefreshAll(WORD /*wNotifyCode*/, WORD /*wID*/, H...
  function CTreeItem (line 100) | CTreeItem CWindowsView::AddNode(HWND hWnd, HTREEITEM hParent) {
  function BOOL (line 155) | BOOL CWindowsView::AddChildNode(HWND hChild) {
  function LRESULT (line 162) | LRESULT CWindowsView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM ...
  function LRESULT (line 198) | LRESULT CWindowsView::OnTimer(UINT, WPARAM id, LPARAM, BOOL&) {
  function LRESULT (line 226) | LRESULT CWindowsView::OnNodeExpanding(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 240) | LRESULT CWindowsView::OnNodeDeleted(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 248) | LRESULT CWindowsView::OnNodeSelected(int, LPNMHDR hdr, BOOL&) {
  function LRESULT (line 263) | LRESULT CWindowsView::OnWindowShow(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 270) | LRESULT CWindowsView::OnWindowHide(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 276) | LRESULT CWindowsView::OnWindowClose(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 282) | LRESULT CWindowsView::OnWindowMinimize(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 288) | LRESULT CWindowsView::OnWindowMaximize(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 295) | LRESULT CWindowsView::OnWindowRestore(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 301) | LRESULT CWindowsView::OnWindowBringToFront(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 307) | LRESULT CWindowsView::OnRefresh(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 312) | LRESULT CWindowsView::OnToggleHiddenWindows(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 319) | LRESULT CWindowsView::OnToggleEmptyTitleWindows(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 326) | LRESULT CWindowsView::OnToggleChildWindows(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 333) | LRESULT CWindowsView::OnTreeNodeRightClick(HTREEITEM hItem, CPoint const...
  function LRESULT (line 344) | LRESULT CWindowsView::OnWindowFlash(WORD, WORD, HWND, BOOL&) {
  function LRESULT (line 351) | LRESULT CWindowsView::OnWindowHighlight(WORD /*wNotifyCode*/, WORD /*wID...
  function LRESULT (line 366) | LRESULT CWindowsView::OnWindowProperties(WORD /*wNotifyCode*/, WORD /*wI...
  function LRESULT (line 378) | LRESULT CWindowsView::OnTreeNodeDoubleClick(HTREEITEM hItem, CPoint cons...
  function LRESULT (line 383) | LRESULT CWindowsView::OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARA...
  function CTreeItem (line 388) | CTreeItem CWindowsView::AddMessageOnlyWindows() {
  function LRESULT (line 394) | LRESULT CWindowsView::OnCaptureMessages(WORD /*wNotifyCode*/, WORD /*wID...

FILE: WinSpyHook/dllmain.cpp
  function BOOL (line 6) | BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, PVOID lpReserved) {

FILE: WinSpyHook/hooks.cpp
  type HookEntry (line 6) | struct HookEntry {
  function IsNotify (line 20) | bool IsNotify(HWND hTarget, HookEntry const& entry) {
  function LRESULT (line 33) | LRESULT WINAPI HookFunc3(int code, WPARAM wParam, LPARAM lParam) {
  function LRESULT (line 58) | LRESULT WINAPI HookFunc4(int code, WPARAM wParam, LPARAM lParam) {
  function LRESULT (line 63) | LRESULT WINAPI HookFunc12(int code, WPARAM wParam, LPARAM lParam) {
  function AddHook (line 68) | bool WINAPI AddHook(DWORD hookType, HookConfig const& config) {
  function RemoveHook (line 99) | bool WINAPI RemoveHook(DWORD tid) {

FILE: WinSpyHook/hooks.h
  function HookOptions (line 5) | enum class HookOptions {
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (248K chars).
[
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".gitignore",
    "chars": 5745,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": ".gitmodules",
    "chars": 91,
    "preview": "[submodule \"WTLHelper\"]\n\tpath = WTLHelper\n\turl = https://github.com/zodiacon/WTLHelper.git\n"
  },
  {
    "path": "LICENSE",
    "chars": 1073,
    "preview": "MIT License\n\nCopyright (c) 2021 Pavel Yosifovich\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "README.md",
    "chars": 59,
    "preview": "# WinSpy\n\nEnhanced version of the classic Spy++ tool (WIP)\n"
  },
  {
    "path": "WinSpy/AboutDlg.cpp",
    "chars": 485,
    "preview": "// aboutdlg.cpp : implementation of the CAboutDlg class\n//\n/////////////////////////////////////////////////////////////"
  },
  {
    "path": "WinSpy/AboutDlg.h",
    "chars": 933,
    "preview": "// aboutdlg.h : interface of the CAboutDlg class\n//\n////////////////////////////////////////////////////////////////////"
  },
  {
    "path": "WinSpy/AutomationTreeView.cpp",
    "chars": 8459,
    "preview": "#include \"pch.h\"\n#include \"AutomationTreeView.h\"\n#include \"WindowHelper.h\"\n\n#pragma comment(lib, \"oleacc\")\n\nLRESULT CAut"
  },
  {
    "path": "WinSpy/AutomationTreeView.h",
    "chars": 2385,
    "preview": "#pragma once\n\n#include \"ViewBase.h\"\n#include <TreeViewHelper.h>\n#include <VirtualListView.h>\n#include <CustomSplitterWin"
  },
  {
    "path": "WinSpy/DialogHelper.h",
    "chars": 1153,
    "preview": "#pragma once\n\ntemplate<typename T>\nclass CDialogHelper {\npublic:\n#ifdef IDI_OK\n\tvoid AdjustOKCancelButtons() {\n\t\tauto dl"
  },
  {
    "path": "WinSpy/FindWindowDlg.cpp",
    "chars": 4040,
    "preview": "#include \"pch.h\"\n#include \"resource.h\"\n#include \"FindWindowDlg.h\"\n#include \"WindowHelper.h\"\n#include \"ProcessHelper.h\"\n#"
  },
  {
    "path": "WinSpy/FindWindowDlg.h",
    "chars": 1929,
    "preview": "#pragma once\n\n#include \"Interfaces.h\"\n#include \"DialogHelper.h\"\n\nclass CFindWindowDlg : \n\tpublic CDialogImpl<CFindWindow"
  },
  {
    "path": "WinSpy/FormatHelper.cpp",
    "chars": 790,
    "preview": "#include \"pch.h\"\n#include \"FormatHelper.h\"\n#include <sstream>\n\nCString FormatHelper::FormatHWndOrNone(HWND hWnd) {\n\tCStr"
  },
  {
    "path": "WinSpy/FormatHelper.h",
    "chars": 227,
    "preview": "#pragma once\n\nstruct FormatHelper {\n\tstatic CString FormatHWndOrNone(HWND hWnd);\n\tstatic CString RectToString(CRect cons"
  },
  {
    "path": "WinSpy/FrameWindowHelper.h",
    "chars": 1171,
    "preview": "#pragma once\n\n#include \"Interfaces.h\"\n\ntemplate<typename T>\nstruct CFrameWindowHelper : CIdleHandler {\n\tHWND CreateAndIn"
  },
  {
    "path": "WinSpy/HookHelper.cpp",
    "chars": 748,
    "preview": "#include \"pch.h\"\n#include \"HookHelper.h\"\n\ndecltype(AddHook)* pAddHook;\ndecltype(RemoveHook)* pRemoveHook;\n\nbool HookHelp"
  },
  {
    "path": "WinSpy/HookHelper.h",
    "chars": 216,
    "preview": "#pragma once\n\n#include \"hooks.h\"\n\nstruct HookHelper abstract final {\n\tstatic bool InitHookLib();\n\tstatic bool WINAPI Add"
  },
  {
    "path": "WinSpy/IconHelper.cpp",
    "chars": 348,
    "preview": "#include \"pch.h\"\n#include \"IconHelper.h\"\n\nHICON IconHelper::GetShieldIcon() {\n\treturn GetStockIcon(SIID_SHIELD);\n}\n\nHICO"
  },
  {
    "path": "WinSpy/IconHelper.h",
    "chars": 132,
    "preview": "#pragma once\n\nstruct IconHelper {\n\tstatic HICON GetStockIcon(SHSTOCKICONID id, bool big = false);\n\tstatic HICON GetShiel"
  },
  {
    "path": "WinSpy/ImageIconCache.cpp",
    "chars": 1236,
    "preview": "#include \"pch.h\"\n#include \"ImageIconCache.h\"\n#include \"resource.h\"\n\nint ImageIconCache::GetIcon(const CString& path, HIC"
  },
  {
    "path": "WinSpy/ImageIconCache.h",
    "chars": 545,
    "preview": "#pragma once\n\nstruct ImageIconCache {\n\tstatic ImageIconCache& Get();\n\n\tHIMAGELIST GetImageList() const;\n\tvoid SetImageLi"
  },
  {
    "path": "WinSpy/Interfaces.h",
    "chars": 414,
    "preview": "#pragma once\n\nstruct CMessagesView;\n\nstruct IMainFrame {\n\tvirtual CUpdateUIBase& GetUIUpdate() = 0;\n\tvirtual UINT ShowPo"
  },
  {
    "path": "WinSpy/MainFrm.cpp",
    "chars": 8851,
    "preview": "// MainFrm.cpp : implmentation of the CMainFrame class\n//\n//////////////////////////////////////////////////////////////"
  },
  {
    "path": "WinSpy/MainFrm.h",
    "chars": 4210,
    "preview": "// MainFrm.h : interface of the CMainFrame class\n//\n////////////////////////////////////////////////////////////////////"
  },
  {
    "path": "WinSpy/MessageDecoder.cpp",
    "chars": 2955,
    "preview": "#include \"pch.h\"\n#include \"MessageDecoder.h\"\n#include \"FormatHelper.h\"\n\n#define CASE_STR(x) case x: return L#x \n\nCString"
  },
  {
    "path": "WinSpy/MessageDecoder.h",
    "chars": 240,
    "preview": "#pragma once\n\nstruct MessageDecoder final abstract {\n\tstatic CString Decode(UINT msg, WPARAM wp, LPARAM lp);\n\tstatic CSt"
  },
  {
    "path": "WinSpy/MessagesView.cpp",
    "chars": 5175,
    "preview": "#include \"pch.h\"\n#include \"MessagesView.h\"\n#include \"hooks.h\"\n#include \"FormatHelper.h\"\n#include \"WindowHelper.h\"\n#inclu"
  },
  {
    "path": "WinSpy/MessagesView.h",
    "chars": 2155,
    "preview": "#pragma once\n\n#include \"ViewBase.h\"\n#include \"VirtualListView.h\"\n#include \"hooks.h\"\n\nstruct CMessagesView;\n\nstruct CHook"
  },
  {
    "path": "WinSpy/ProcessHelper.cpp",
    "chars": 3649,
    "preview": "#include \"pch.h\"\n#include \"ProcessHelper.h\"\n#include \"WindowHelper.h\"\n#include <TlHelp32.h>\n#include <unordered_set>\n\nCS"
  },
  {
    "path": "WinSpy/ProcessHelper.h",
    "chars": 754,
    "preview": "#pragma once\n\nstruct ProcessInfo {\n\tDWORD ProcessId;\n\tstd::vector<DWORD> Threads;\n\tCString ProcessName;\n\tCString FullPat"
  },
  {
    "path": "WinSpy/ProcessesView.cpp",
    "chars": 8848,
    "preview": "#include \"pch.h\"\n#include \"resource.h\"\n#include \"ProcessesView.h\"\n#include \"ImageIconCache.h\"\n\nLRESULT CProcessesView::O"
  },
  {
    "path": "WinSpy/ProcessesView.h",
    "chars": 4154,
    "preview": "#pragma once\n\n#include \"ViewBase.h\"\n#include \"WindowsListView.h\"\n#include \"TreeViewManager.h\"\n#include \"ProcessHelper.h\""
  },
  {
    "path": "WinSpy/SecurityHelper.cpp",
    "chars": 1363,
    "preview": "#include \"pch.h\"\n#include \"SecurityHelper.h\"\n\nbool SecurityHelper::IsRunningElevated() {\n\tstatic bool runningElevated = "
  },
  {
    "path": "WinSpy/SecurityHelper.h",
    "chars": 185,
    "preview": "#pragma once\n\nstruct SecurityHelper abstract final {\n\tstatic bool IsRunningElevated();\n\tstatic bool RunElevated();\n\tstat"
  },
  {
    "path": "WinSpy/TreeViewManager.h",
    "chars": 1300,
    "preview": "#pragma once\n\ntemplate<typename T>\nclass CTreeViewManager {\nprotected:\n\tBEGIN_MSG_MAP(CTreeViewManager)\n\t\tNOTIFY_CODE_HA"
  },
  {
    "path": "WinSpy/ViewBase.h",
    "chars": 2062,
    "preview": "#pragma once\n\n#include \"Interfaces.h\"\n\ntemplate<typename T, typename TBase = CFrameWindowImpl<T, CWindow, CControlWinTra"
  },
  {
    "path": "WinSpy/WinSpy.cpp",
    "chars": 1005,
    "preview": "// WinSpy.cpp : main source file for WinSpy.exe\n//\n\n#include \"pch.h\"\n#include \"resource.h\"\n#include \"MainFrm.h\"\n#include"
  },
  {
    "path": "WinSpy/WinSpy.h",
    "chars": 12,
    "preview": "// WinSpy.h\n"
  },
  {
    "path": "WinSpy/WinSpy.rc",
    "chars": 21034,
    "preview": "// Microsoft Visual C++ generated resource script.\n//\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////"
  },
  {
    "path": "WinSpy/WinSpy.vcxproj",
    "chars": 21514,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msb"
  },
  {
    "path": "WinSpy/WinSpy.vcxproj.filters",
    "chars": 7853,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuil"
  },
  {
    "path": "WinSpy/WindowGeneralPage.cpp",
    "chars": 4350,
    "preview": "#include \"pch.h\"\n#include \"resource.h\"\n#include \"WindowGeneralPage.h\"\n#include \"FormatHelper.h\"\n#include \"ProcessHelper."
  },
  {
    "path": "WinSpy/WindowGeneralPage.h",
    "chars": 1229,
    "preview": "#pragma once\n\n#include \"DialogHelper.h\"\n\nstruct StyleItem;\n\nclass CWindowGeneralPage :\n\tpublic CDialogHelper<CWindowGene"
  },
  {
    "path": "WinSpy/WindowHelper.cpp",
    "chars": 21688,
    "preview": "#include \"pch.h\"\n#include \"resource.h\"\n#include \"WindowHelper.h\"\n#include \"ProcessHelper.h\"\n#include \"FormatHelper.h\"\n#i"
  },
  {
    "path": "WinSpy/WindowHelper.h",
    "chars": 2196,
    "preview": "#pragma once\n\nstruct WindowItem {\n\tHWND hWnd;\n\tDWORD ThreadId;\n\tDWORD ProcessId;\n\tCString ProcessName;\n};\n\nstruct StyleI"
  },
  {
    "path": "WinSpy/WindowWindowsPage.cpp",
    "chars": 1592,
    "preview": "#include \"pch.h\"\n#include \"resource.h\"\n#include \"WindowWindowsPage.h\"\n#include \"FormatHelper.h\"\n\nconst UINT WM_UPDATE = "
  },
  {
    "path": "WinSpy/WindowWindowsPage.h",
    "chars": 1044,
    "preview": "#pragma once\n\n#include \"DialogHelper.h\"\n\nclass CWindowWindowsPage :\n\tpublic CDialogHelper<CWindowWindowsPage>,\n\tpublic C"
  },
  {
    "path": "WinSpy/WindowsListView.cpp",
    "chars": 14611,
    "preview": "#include \"pch.h\"\n#include \"resource.h\"\n#include \"WindowsListView.h\"\n#include \"FormatHelper.h\"\n#include \"SortHelper.h\"\n#i"
  },
  {
    "path": "WinSpy/WindowsListView.h",
    "chars": 4388,
    "preview": "#pragma once\n\n#include <VirtualListView.h>\n#include \"Interfaces.h\"\n#include \"WindowHelper.h\"\n#include \"ProcessHelper.h\"\n"
  },
  {
    "path": "WinSpy/WindowsView.cpp",
    "chars": 10355,
    "preview": "// View.cpp : implementation of the CWindowsView class\n//\n//////////////////////////////////////////////////////////////"
  },
  {
    "path": "WinSpy/WindowsView.h",
    "chars": 5268,
    "preview": "// View.h : interface of the CView class\n//\n////////////////////////////////////////////////////////////////////////////"
  },
  {
    "path": "WinSpy/pch.cpp",
    "chars": 194,
    "preview": "// stdafx.cpp : source file that includes just the standard includes\n//\tWinSpy.pch will be the pre-compiled header\n//\tst"
  },
  {
    "path": "WinSpy/pch.h",
    "chars": 1636,
    "preview": "// stdafx.h : include file for standard system include files,\n//  or project specific include files that are used freque"
  },
  {
    "path": "WinSpy/resource.h",
    "chars": 4578,
    "preview": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by WinSpy.rc\n//\n#define IDD_ABOUTBOX      "
  },
  {
    "path": "WinSpy.sln",
    "chars": 5631,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.3.3251"
  },
  {
    "path": "WinSpyHook/WinSpyHook.def",
    "chars": 98,
    "preview": "LIBRARY\nEXPORTS\n\tHookFunc3\t\tPRIVATE\n\tHookFunc4\t\tPRIVATE\n\tHookFunc12\t\tPRIVATE\n\tAddHook\n\tRemoveHook\n"
  },
  {
    "path": "WinSpyHook/WinSpyHook.vcxproj",
    "chars": 13333,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msb"
  },
  {
    "path": "WinSpyHook/WinSpyHook.vcxproj.filters",
    "chars": 1447,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuil"
  },
  {
    "path": "WinSpyHook/dllmain.cpp",
    "chars": 320,
    "preview": "// dllmain.cpp : Defines the entry point for the DLL application.\n#include \"pch.h\"\n\nHINSTANCE g_hInstDll;\n\nBOOL WINAPI D"
  },
  {
    "path": "WinSpyHook/hooks.cpp",
    "chars": 2882,
    "preview": "#include \"pch.h\"\n#include \"hooks.h\"\n\nconst unsigned MaxHooks = 8;\n\nstruct HookEntry {\n\tHHOOK hHook;\n\tHWND CallbackWnd;\n\t"
  },
  {
    "path": "WinSpyHook/hooks.h",
    "chars": 498,
    "preview": "#pragma once\n\nconst UINT WM_HOOKCALLBACK = WM_USER + 333;\n\nenum class HookOptions {\n\tNone = 0,\n\tWindow = 1,\n\tChildWindow"
  },
  {
    "path": "WinSpyHook/pch.cpp",
    "chars": 186,
    "preview": "// pch.cpp: source file corresponding to the pre-compiled header\n\n#include \"pch.h\"\n\n// When you are using pre-compiled h"
  },
  {
    "path": "WinSpyHook/pch.h",
    "chars": 610,
    "preview": "// pch.h: This is a precompiled header file.\n// Files listed below are compiled only once, improving build performance f"
  }
]

About this extraction

This page contains the full source code of the zodiacon/WinSpy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 63 files (224.8 KB), approximately 67.5k tokens, and a symbol index with 193 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!