Skip to content

Commit

Permalink
display some properties of automation objects
Browse files Browse the repository at this point in the history
  • Loading branch information
zodiacon committed Mar 26, 2024
1 parent 3ef4049 commit e22d702
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 24 deletions.
159 changes: 142 additions & 17 deletions WinSpy/AutomationTreeView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ LRESULT CAutomationTreeView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /
LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL);
m_List.SetExtendedListViewStyle(LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT);

m_List.InsertColumn(0, L"Property", 0, 150);
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);
Expand Down Expand Up @@ -67,6 +67,14 @@ LRESULT CAutomationTreeView::OnNodeExpanded(int, LPNMHDR hdr, BOOL&) {
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);
Expand All @@ -83,20 +91,20 @@ void CAutomationTreeView::OnActivate(bool active) {
}

CString CAutomationTreeView::GetColumnText(HWND, int row, int col) const {
return CString();
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);
int pid = 0;
e->get_CurrentProcessId(&pid);
e->get_CurrentAutomationId(&id);
UIA_HWND hWnd = nullptr;
e->get_CurrentNativeWindowHandle(&hWnd);
CString text;
text.Format(L"%s [%s] (ID: %s, PID: %d, HWND: 0x%p)", name.m_str, cls.m_str, id.m_str, pid, hWnd);
text.Format(L"%s [%s]", name.m_str, cls.m_str);

auto hItem = m_Tree.InsertItem(text, hParent, hAfter);
e->AddRef();
Expand All @@ -112,7 +120,6 @@ void CAutomationTreeView::EnumChildElements(IUIAutomationTreeWalker* pWalker, IU
spElem->get_CurrentProcessId(&pid);
if (pid != ::GetCurrentProcessId()) {
auto node = AddElement(spElem, hParent, hAfter);
//EnumChildElements(pWalker, spElem, node, TVI_LAST, depth + 1);
CComPtr<IUIAutomationElement> spChild;
pWalker->GetFirstChildElement(spElem, &spChild);
if (spChild) {
Expand All @@ -131,21 +138,20 @@ void CAutomationTreeView::EnumChildElements(IUIAutomationTreeWalker* pWalker, IU

void CAutomationTreeView::InitTree() {
CWaitCursor wait;
CComPtr<IUIAutomation> spUI;
spUI.CoCreateInstance(__uuidof(CUIAutomation));
ATLASSERT(spUI);
if (spUI == nullptr)
return;

if (m_spUI == nullptr) {
m_spUI.CoCreateInstance(__uuidof(CUIAutomation));
if (m_spUI == nullptr)
return;
}
m_Tree.SetRedraw(FALSE);
m_Tree.DeleteAllItems();
CComPtr<IUIAutomationTreeWalker> spWalker;
spUI->get_RawViewWalker(&spWalker);
m_spUI->get_RawViewWalker(&spWalker);
ATLASSERT(spWalker);
m_spUIWalker = spWalker;

CComPtr<IUIAutomationElement> spRoot;
spUI->GetRootElement(&spRoot);
m_spUI->GetRootElement(&spRoot);
auto node = AddElement(spRoot);

EnumChildElements(spWalker, spRoot, node);
Expand All @@ -155,3 +161,122 @@ void CAutomationTreeView::InitTree() {

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"";
}
12 changes: 10 additions & 2 deletions WinSpy/AutomationTreeView.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class 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_SELCHANGED, OnNodeSelected)
NOTIFY_CODE_HANDLER(TVN_DELETEITEM, OnNodeDeleted)
CHAIN_MSG_MAP(CTreeViewHelper<CAutomationTreeView>)
CHAIN_MSG_MAP(CVirtualListView<CAutomationTreeView>)
Expand All @@ -43,17 +43,25 @@ class CAutomationTreeView :

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 OnWindowProperties(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;
};
10 changes: 5 additions & 5 deletions WinSpy/WinSpy.rc
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ END
//

VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,2,5,0
PRODUCTVERSION 0,2,5,0
FILEVERSION 0,3,0,0
PRODUCTVERSION 0,3,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
Expand All @@ -422,12 +422,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Scorpio Software"
VALUE "FileDescription", "WinSpy - UI Windows Properties and Messages"
VALUE "FileVersion", "0.2.5.0"
VALUE "FileVersion", "0.3.0.0"
VALUE "InternalName", "WinSpy"
VALUE "LegalCopyright", "�2021-2023 Pavel Yosifovich"
VALUE "LegalCopyright", "�2021-2024 Pavel Yosifovich"
VALUE "OriginalFilename", "WinSpy.exe"
VALUE "ProductName", "WinSpy"
VALUE "ProductVersion", "0.2.5.0"
VALUE "ProductVersion", "0.3.0.0"
END
END
BLOCK "VarFileInfo"
Expand Down
1 change: 1 addition & 0 deletions WinSpy/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern CAppModule _Module;
#include <unordered_map>
#include <algorithm>
#include <mutex>
#include <format>

#if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
Expand Down

0 comments on commit e22d702

Please sign in to comment.