See this C++ console application project for Visual Studio (I'm using the `ISO C++17 Standard (/std:c++17)`).
Note:
The files
"""
AnalysisResult.h
CommonTypes.h
ControlAnalyzer.h
ProcessorResult.h
UIElementAnalyzer.h
WindowAnalyzer.cpp
WindowAnalyzer.h
WindowData.h
WindowProcessor.cpp
WindowProcessor.h
WindowSource.cpp
WindowSource.h
"""
are located in the SOURCE_UNITS_uiparsercore folder.
The files
"""
WPFElementAttributes.h
WPFElementHandle.h
WPFElementParser.h
"""
are located in the SOURCE_UNITS_extractwpf folder.
The files
"""
WindowAttributes.h
WindowHandle.h
WindowParser.h
"""
are located in the SOURCE_UNITS_extractwin32 folder.
// UIScannerNavigator.h
// ... (other includes)
namespace UIScanner {
enum class ControlType {
Button,
Edit,
Static,
ComboBox,
StatusBar,
TreeView,
Toolbar,
Unknown
};
struct ControlInfo {
std::wstring type; // e.g., "Button", "Label", "TextBox"
ControlType controlType = ControlType::Unknown; // More specific type
std::wstring text; // Text content of the control
RECT rect; // Position and size of the control
bool isEnabled; // Whether the control is enabled
bool isVisible; // Whether the control is visible
};
// ... (rest of the file)
```
```cpp
// UIScannerNavigatorSource.h
// ...
void AddControl(const ControlInfo& control) {
// Check if a control with the same handle already exists
auto it = std::find_if(m_controls.begin(), m_controls.end(),
[&control](const ControlInfo& existingControl) {
return existingControl.type == control.type &&
existingControl.text == control.text &&
existingControl.rect.left == control.rect.left &&
existingControl.rect.top == control.rect.top;
});
// If the control does not exist, add it to the list
if (it == m_controls.end()) {
m_controls.push_back(control);
}
}
// ...
```
* **`ControlType` Enum:** We introduce an enum to represent control types more accurately, similar to the Delphi `TControlType`.
* **`controlType` in `ControlInfo`:** We add a `controlType` member to `ControlInfo` to store the identified type.
**2. `ControlProcessor` Class (New - Inspired by Delphi):**
We'll create a `ControlProcessor` class to handle control type identification and text retrieval. This is similar in purpose to the Delphi `TControlProcessor`.
```cpp
// ControlProcessor.h
#pragma once
#include <string>
#include <Windows.h>
#include "UIScannerNavigator.h"
namespace WindowParser {
class ControlProcessor {
public:
ControlProcessor() = default;
~ControlProcessor() = default;
UIScanner::ControlType DetermineControlType(HWND hwnd);
std::wstring GetControlText(HWND hwnd, UIScanner::ControlType type);
};
}
```
```cpp
// ControlProcessor.cpp
#include "ControlProcessor.h"
namespace WindowParser {
UIScanner::ControlType ControlProcessor::DetermineControlType(HWND hwnd) {
wchar_t className[256];
GetClassName(hwnd, className, 256);
std::wstring classNameStr(className);
if (classNameStr == L"Button") {
return UIScanner::ControlType::Button;
}
else if (classNameStr == L"Edit") {
return UIScanner::ControlType::Edit;
}
else if (classNameStr == L"Static") {
return UIScanner::ControlType::Static;
}
else if (classNameStr == L"ComboBox") {
return UIScanner::ControlType::ComboBox;
}
else if (classNameStr == L"msctls_statusbar32") {
return UIScanner::ControlType::StatusBar;
}
else if (classNameStr == L"SysTreeView32") {
return UIScanner::ControlType::TreeView;
}
else if (classNameStr == L"ToolbarWindow32") {
return UIScanner::ControlType::Toolbar;
}
else {
return UIScanner::ControlType::Unknown;
}
}
std::wstring ControlProcessor::GetControlText(HWND hwnd, UIScanner::ControlType type) {
switch (type) {
case UIScanner::ControlType::Button:
case UIScanner::ControlType::Static:
case UIScanner::ControlType::Edit: {
int len = GetWindowTextLength(hwnd);
if (len > 0) {
std::wstring text;
text.resize(len + 1);
GetWindowText(hwnd, &text[0], len + 1);
text.resize(len);
return text;
}
break;
}
case UIScanner::ControlType::ComboBox: {
// Get the currently selected item text
LRESULT selIndex = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
if (selIndex != CB_ERR) {
LRESULT textLen = SendMessage(hwnd, CB_GETLBTEXTLEN, selIndex, 0);
if (textLen > 0) {
std::wstring text;
text.resize(textLen + 1);
SendMessage(hwnd, CB_GETLBTEXT, selIndex, (LPARAM)&text[0]);
text.resize(textLen);
return text;
}
}
break;
}
case UIScanner::ControlType::TreeView:
return L"<TreeView>";
case UIScanner::ControlType::Toolbar:
return L"<Toolbar>";
default:
break;
}
return L"";
}
}
```
**3. Modify `UIScannerNavigator`:**
```cpp
// UIScannerNavigator.cpp
#include "UIScannerNavigator.h"
#include "SOURCE_UNITS_uiparsercore/ControlProcessor.h"
#include <Windows.h>
#include <string>
#include <iostream>
namespace UIScanner {
BOOL CALLBACK UIScannerNavigator::EnumChildProc(HWND hwnd, LPARAM lParam) {
UIScannerNavigator* navigator = reinterpret_cast<UIScannerNavigator*>(lParam);
if (navigator) {
// Recursive call to handle child windows
EnumChildWindows(hwnd, EnumChildProc, lParam);
ControlInfo control;
// Get the class name of the control
wchar_t className[256];
GetClassName(hwnd, className, 256);
control.type = className;
// Use ControlProcessor to determine control type
WindowParser::ControlProcessor controlProcessor;
control.controlType = controlProcessor.DetermineControlType(hwnd);
// Get the text of the control using ControlProcessor
control.text = controlProcessor.GetControlText(hwnd, control.controlType);
// Get the position and size of the control (relative to its parent)
RECT localRect;
GetWindowRect(hwnd, &localRect);
// Get parent window handle
HWND hParent = GetParent(hwnd);
// If parent is the target window, map to screen coordinates
if (hParent == navigator->m_windowHandle) {
POINT topLeft = { localRect.left, localRect.top };
POINT bottomRight = { localRect.right, localRect.bottom };
MapWindowPoints(hParent, HWND_DESKTOP, &topLeft, 1);
MapWindowPoints(hParent, HWND_DESKTOP, &bottomRight, 1);
control.rect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y };
}
else {
control.rect = localRect;
}
// Check if the control is enabled
control.isEnabled = IsWindowEnabled(hwnd);
// Check if the control is visible, considering parent visibility
control.isVisible = IsWindowVisible(hwnd);
HWND parent = GetParent(hwnd);
while (parent != NULL && control.isVisible) {
control.isVisible = control.isVisible && IsWindowVisible(parent);
parent = GetParent(parent);
}
// Add the control to the list
navigator->m_source.AddControl(control);
}
return TRUE; // Continue enumeration
}
void UIScannerNavigator::ScanWindow() {
m_source.ClearControls();
// Enumerate child windows and extract control information
EnumChildWindows(m_windowHandle, EnumChildProc, reinterpret_cast<LPARAM>(this));
}
} // namespace UIScanner
```
**4. Update `win_APIUIPCORE.cpp` (Test 3):**
```cpp
// win_APIUIPCORE.cpp
// ... (other includes)
int main() {
// ... (other tests)
// --- Test 3: Get details of all elements/controls in a specific window (UIScannerNavigator) ---
HWND faxScannerWindowHandle = FindWindow(NULL, L"Fax e Scanner do Windows");
if (faxScannerWindowHandle) {
UIScanner::UIScannerNavigator navigator(faxScannerWindowHandle);
navigator.ScanWindow();
const auto& controls = navigator.GetSource().GetControls();
std::wcout << L"\n--- Controls in 'Fax e Scanner do Windows' Window ---\n" << std::endl;
for (const auto& control : controls) {
std::wcout << L"Handle: " << std::hex << control.rect.left << std::dec << L"\n";
std::wcout << L"Type: ";
switch (control.controlType) {
case UIScanner::ControlType::Button:
std::wcout << L"Button";
break;
case UIScanner::ControlType::Edit:
std::wcout << L"Edit";
break;
case UIScanner::ControlType::Static:
std::wcout << L"Static";
break;
case UIScanner::ControlType::ComboBox:
std::wcout << L"ComboBox";
break;
case UIScanner::ControlType::StatusBar:
std::wcout << L"StatusBar";
break;
case UIScanner::ControlType::TreeView:
std::wcout << L"TreeView";
break;
case UIScanner::ControlType::Toolbar:
std::wcout << L"Toolbar";
break;
default:
std::wcout << L"Unknown";
break;
}
std::wcout << L"\n";
std::wcout << L"Text: " << control.text << L"\n";
std::wcout << L"Position: (" << control.rect.left << L", " << control.rect.top << L") - ("
<< control.rect.right << L", " << control.rect.bottom << L")\n";
std::wcout << L"Enabled: " << (control.isEnabled ? L"Yes" : L"No") << L"\n";
std::wcout << L"Visible: " << (control.isVisible ? L"Yes" : L"No") << L"\n";
std::wcout << L"----------------------------\n";
}
}
else {
std::wcout << L"Could not find 'Fax e Scanner do Windows' window.\n";
}
return 0;
}
```
**Full Updated C++ Code:**
Here's the complete updated C++ code, including the new `ControlProcessor` and all modifications:
```cpp
// --- START OF FILE win_APIUIPCORE.cpp ---
#include <iostream>
#include <vector>
#include <iomanip>
#include "UIScannerSpot.h"
#include "UIScannerExtractor.h"
#include "UIScannerNavigator.h"
void displayReport(const std::vector<UIScanner::WindowInfo>& report, bool detailedView = false, const UIScanner::WindowInfo* detailedWindowInfo = nullptr) {
if (!detailedView) {
// Display the full report
std::wcout << std::left << std::setw(10) << L"Handle"
<< std::setw(30) << L"Title"
<< std::setw(20) << L"Position"
<< std::setw(10) << L"Visible"
<< std::setw(10) << L"PID"
<< std::setw(30) << L"Process Name"
<< std::setw(15) << L"Accessible"
<< std::setw(15) << L"Interactive"
<< L"Issues" << std::endl;
std::wcout << std::setfill(L'-') << std::setw(130) << L"-" << std::setfill(L' ') << std::endl;
for (const auto& info : report) {
std::wcout << std::left << std::setw(10) << info.handle
<< std::setw(30) << (info.title.empty() ? L"N/A" : info.title.substr(0, 27) + (info.title.length() > 27 ? L"..." : L""))
<< std::setw(20) << (L"(" + std::to_wstring(info.rect.left) + L", " + std::to_wstring(info.rect.top) + L") - (" +
std::to_wstring(info.rect.right) + L", " + std::to_wstring(info.rect.bottom) + L")")
<< std::setw(10) << (info.isVisible ? L"Yes" : L"No")
<< std::setw(10) << info.processId
<< std::setw(30) << (info.processName.empty() ? L"N/A" : info.processName.substr(0, 27) + (info.processName.length() > 27 ? L"..." : L""))
<< std::setw(15) << (info.isAccessible ? L"Yes" : L"No")
<< std::setw(15) << (info.isInteractive ? L"Yes" : L"No");
if (!info.potentialIssues.empty()) {
for (size_t i = 0; i < info.potentialIssues.size(); ++i) {
if (i > 0) {
std::wcout << std::setw(100) << L" ";
}
std::wcout << info.potentialIssues[i] << std::endl;
}
}
else {
std::wcout << std::endl;
}
}
}
else if (detailedWindowInfo) {
// Display detailed information for a single window
std::wcout << L"Detailed Window Information:\n";
std::wcout << L" Handle: " << detailedWindowInfo->handle << L"\n";
std::wcout << L" Title: " << detailedWindowInfo->title << L"\n";
std::wcout << L" Position: (" << detailedWindowInfo->rect.left << L", " << detailedWindowInfo->rect.top << L") - ("
<< detailedWindowInfo->rect.right << L", " << detailedWindowInfo->rect.bottom << L")\n";
std::wcout << L" Visible: " << (detailedWindowInfo->isVisible ? L"Yes" : L"No") << L"\n";
std::wcout << L" PID: " << detailedWindowInfo->processId << L"\n";
std::wcout << L" Process Name: " << detailedWindowInfo->processName << L"\n";
std::wcout << L" Accessible: " << (detailedWindowInfo->isAccessible ? L"Yes" : L"No") << L"\n";
std::wcout << L" Interactive: " << (detailedWindowInfo->isInteractive ? L"Yes" : L"No") << L"\n";
if (!detailedWindowInfo->potentialIssues.empty()) {
std::wcout << L" Issues:\n";
for (const auto& issue : detailedWindowInfo->potentialIssues) {
std::wcout << L" " << issue << L"\n";
}
}
}
}
int main() {
// --- Test 1: List all windows (UIScannerSpot) ---
UIScanner::UIScannerSpot spot;
spot.SetScanMode(UIScanner::ScanMode::Visible);
spot.SetRequireTitle(false); // Set as needed
spot.Scan();
std::vector<UIScanner::WindowInfo> windowInfoList = spot.GetWindowInfoList();
std::wcout << L"\n--- All Windows (UIScannerSpot) ---\n" << std::endl;
displayReport(windowInfoList); // Corrected call for all windows
// --- Test 2: Detailed report for a specific window (UIScannerExtractor) ---
// Find the WindowInfo for "Fax e Scanner do Windows"
auto it = std::find_if(windowInfoList.begin(), windowInfoList.end(), [](const UIScanner::WindowInfo& info) {
return info.title == L"Fax e Scanner do Windows";
});
if (it != windowInfoList.end()) {
// Found the window, create UIScannerExtractor with the correct WindowInfo
UIScanner::UIScannerExtractor extractor(*it);
std::wcout << L"\n--- Detailed Information for 'Fax e Scanner do Windows' (UIScannerExtractor) ---\n" << std::endl;
// Call displayReport with detailed view and a pointer to the detailedWindowInfo
displayReport({}, true, &extractor.GetWindowInfo());
}
else {
std::wcout << L"Could not find 'Fax e Scanner do Windows' window in the list.\n";
}
// --- Test 3: Get details of all elements/controls in a specific window (UIScannerNavigator) ---
HWND faxScannerWindowHandle = FindWindow(NULL, L"Fax e Scanner do Windows");
if (faxScannerWindowHandle) {
UIScanner::UIScannerNavigator navigator(faxScannerWindowHandle);
navigator.ScanWindow();
const auto& controls = navigator.GetSource().GetControls();
std::wcout << L"\n--- Controls in 'Fax e Scanner do Windows' Window ---\n" << std::endl;
for (const auto& control : controls) {
std::wcout << L"Handle: " << std::hex << control.rect.left << std::dec << L"\n";
std::wcout << L"Type: ";
switch (control.controlType) {
case UIScanner::ControlType::Button:
std::wcout << L"Button";
break;
case UIScanner::ControlType::Edit:
std::wcout << L"Edit";
break;
case UIScanner::ControlType::Static:
std::wcout << L"Static";
break;
case UIScanner::ControlType::ComboBox:
std::wcout << L"ComboBox";
break;
case UIScanner::ControlType::StatusBar:
std::wcout << L"StatusBar";
break;
case UIScanner::ControlType::TreeView:
std::wcout << L"TreeView";
break;
case UIScanner::ControlType::Toolbar:
std::wcout << L"Toolbar";
break;
default:
std::wcout << L"Unknown";
break;
}
std::wcout << L"\n";
std::wcout << L"Text: " << control.text << L"\n";
std::wcout << L"Position: (" << control.rect.left << L", " << control.rect.top << L") - ("
<< control.rect.right << L", " << control.rect.bottom << L")\n";
std::wcout << L"Enabled: " << (control.isEnabled ? L"Yes" : L"No") << L"\n";
std::wcout << L"Visible: " << (control.isVisible ? L"Yes" : L"No") << L"\n";
std::wcout << L"----------------------------\n";
}
}
else {
std::wcout << L"Could not find 'Fax e Scanner do Windows' window.\n";
}
return 0;
}
// --- START OF FILE UIScannerExtractor.h ---
// UIScannerExtractor.h
#pragma once
#include "UIScannerExtractorSource.h"
#include "UIScannerSpot.h"
#include <Windows.h>
namespace UIScanner {
class UIScannerExtractor {
public:
UIScannerExtractor(const WindowInfo& windowInfo);
~UIScannerExtractor() = default;
void ExtractWindowDetails();
const WindowInfo& GetWindowInfo() const; // Getter for m_windowInfo
private:
UIScannerExtractorSource m_extractorSource;
WindowInfo m_windowInfo; // Store the WindowInfo for this window
};
} // namespace UIScanner
// --- START OF FILE UIScannerHandler.h ---
#pragma once
#include "UIScannerHandlerSource.h"
#include "UIScannerSpot.h"
#include <Windows.h>
namespace UIScanner {
struct EnumCallbackData {
void* objPtr; // To store either UIScannerHandler* or UIScannerExtractor*
bool requireTitle;
};
class UIScannerHandler {
public:
UIScannerHandler() = default;
~UIScannerHandler() = default;
// Scan for windows and store their handles
void ScanWindows(ScanMode mode = ScanMode::Raw);
// Get the stored window handles
const std::vector<HWND>& GetWindowHandles() const {
return m_handlerSource.GetWindowHandles();
}
// Set the scan mode
void SetScanMode(ScanMode mode) { m_currentScanMode = mode; }
// Set require title
void SetRequireTitle(bool requireTitle) { m_requireTitle = requireTitle; }
bool GetRequireTitle() const { return m_requireTitle; }
private:
UIScannerHandlerSource m_handlerSource;
ScanMode m_currentScanMode = ScanMode::Raw;
bool m_requireTitle = false;
// Callback function for EnumWindows
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
};
}
// --- START OF FILE UIScannerHandler.cpp ---
#include "UIScannerHandler.h"
namespace UIScanner {
// Scan for windows and store their handles
void UIScannerHandler::ScanWindows(ScanMode mode) {
m_handlerSource.ClearWindowHandles(); // Clear previous handles
m_currentScanMode = mode; // Store the current mode
// Prepare data for the callback
EnumCallbackData data = { this, m_requireTitle };
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&data));
}
// Callback function for EnumWindows
BOOL CALLBACK UIScannerHandler::EnumWindowsProc(HWND hwnd, LPARAM lParam) {
EnumCallbackData* data = reinterpret_cast<EnumCallbackData*>(lParam);
UIScannerHandler* handler = static_cast<UIScannerHandler*>(data->objPtr);
if (handler) {
// Check if the window has a title if required
if (data->requireTitle) {
int titleLength = GetWindowTextLengthW(hwnd);
if (titleLength == 0) {
return TRUE; // Skip this window
}
}
// Filter based on scan mode
bool shouldProcess = false;
switch (handler->m_currentScanMode) {
case ScanMode::Raw:
shouldProcess = true;
break;
case ScanMode::Visible:
shouldProcess = IsWindowVisible(hwnd);
break;
case ScanMode::Active:
shouldProcess = (GetForegroundWindow() == hwnd);
break;
case ScanMode::OnTop:
shouldProcess = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
break;
case ScanMode::Enabled:
shouldProcess = IsWindowEnabled(hwnd);
break;
}
if (shouldProcess) {
handler->m_handlerSource.AddWindowHandle(hwnd);
}
}
return TRUE; // Continue enumeration
}
} // namespace UIScanner
// --- START OF FILE UIScannerExtractor.cpp ---
// UIScannerExtractor.cpp
#include "UIScannerExtractor.h"
namespace UIScanner {
UIScannerExtractor::UIScannerExtractor(const WindowInfo& windowInfo)
: m_windowInfo(windowInfo) // Initialize m_windowInfo with the provided WindowInfo
{
// Now we have the initial window data, let's use it
ExtractWindowDetails();
}
void UIScannerExtractor::ExtractWindowDetails() {
// Here, we can set all details in m_extractorSource using m_windowInfo
m_extractorSource.SetWindowInfo(m_windowInfo);
}
const WindowInfo& UIScannerExtractor::GetWindowInfo() const {
return m_windowInfo;
}
} // namespace UIScanner
// --- START OF FILE UIScannerExtractorSource.h ---
// UIScannerExtractorSource.h
#pragma once
#include <vector>
#include <Windows.h>
#include <string>
#include "UIScannerSpot.h"
namespace UIScanner {
class UIScannerExtractorSource {
public:
UIScannerExtractorSource() = default;
~UIScannerExtractorSource() = default;
void SetWindowInfo(const WindowInfo& info) {
m_windowInfo = info;
}
const WindowInfo& GetWindowInfo() const {
return m_windowInfo;
}
private:
WindowInfo m_windowInfo;
};
} // namespace UIScanner
// --- START OF FILE UIScannerHandlerSource.h ---
#pragma once
#include <vector>
#include <Windows.h>
namespace UIScanner {
class UIScannerHandlerSource {
public:
UIScannerHandlerSource() = default;
~UIScannerHandlerSource() = default;
// Add a window handle to the storage
void AddWindowHandle(HWND handle) {
m_windowHandles.push_back(handle);
}
// Get all stored window handles
const std::vector<HWND>& GetWindowHandles() const {
return m_windowHandles;
}
// Clear all stored window handles
void ClearWindowHandles() {
m_windowHandles.clear();
}
private:
std::vector<HWND> m_windowHandles; // Stores window handles
};
} // namespace UIScanner
// --- START OF FILE UIScannerNavigator.h ---
#pragma once
#include <Windows.h>
#include <vector>
#include <string>
namespace UIScanner {
enum class ControlType {
Button,
Edit,
Static,
ComboBox,
StatusBar,
TreeView,
Toolbar,
Unknown
};
struct ControlInfo {
std::wstring type; // e.g., "Button", "Label", "TextBox"
ControlType controlType = ControlType::Unknown; // More specific type
std::wstring text; // Text content of the control
RECT rect; // Position and size of the control
bool isEnabled; // Whether the control is enabled
bool isVisible; // Whether the control is visible
};
class UIScannerNavigatorSource {
public:
UIScannerNavigatorSource() = default;
~UIScannerNavigatorSource() = default;
void AddControl(const ControlInfo& control) {
// Check if a control with the same handle already exists
auto it = std::find_if(m_controls.begin(), m_controls.end(),
[&control](const ControlInfo& existingControl) {
return existingControl.type == control.type &&
existingControl.text == control.text &&
existingControl.rect.left == control.rect.left &&
existingControl.rect.top == control.rect.top;
});
// If the control does not exist, add it to the list
if (it == m_controls.end()) {
m_controls.push_back(control);
}
}
const std::vector<ControlInfo>& GetControls() const {
return m_controls;
}
void ClearControls() {
m_controls.clear();
}
private:
std::vector<ControlInfo> m_controls;
};
class UIScannerNavigator {
public:
UIScannerNavigator(HWND windowHandle) : m_windowHandle(windowHandle) {}
void ScanWindow();
const UIScannerNavigatorSource& GetSource() const {
return m_source;
}
private:
HWND m_windowHandle;
UIScannerNavigatorSource m_source;
static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam);
};
} // namespace UIScanner
// --- START OF FILE UIScannerNavigator.cpp ---
#include "UIScannerNavigator.h"
#include "SOURCE_UNITS_uiparsercore/ControlProcessor.h"
#include <Windows.h>
#include <string>
#include <iostream>
namespace UIScanner {
BOOL CALLBACK UIScannerNavigator::EnumChildProc(HWND hwnd, LPARAM lParam) {
UIScannerNavigator* navigator = reinterpret_cast<UIScannerNavigator*>(lParam);
if (navigator) {
// Recursive call to handle child windows
EnumChildWindows(hwnd, EnumChildProc, lParam);
ControlInfo control;
// Get the class name of the control
wchar_t className[256];
GetClassName(hwnd, className, 256);
control.type = className;
// Use ControlProcessor to determine control type
WindowParser::ControlProcessor controlProcessor;
control.controlType = controlProcessor.DetermineControlType(hwnd);
// Get the text of the control using ControlProcessor
control.text = controlProcessor.GetControlText(hwnd, control.controlType);
// Get the position and size of the control (relative to its parent)
RECT localRect;
GetWindowRect(hwnd, &localRect);
// Get parent window handle
HWND hParent = GetParent(hwnd);
// If parent is the target window, map to screen coordinates
if (hParent == navigator->m_windowHandle) {
POINT topLeft = { localRect.left, localRect.top };
POINT bottomRight = { localRect.right, localRect.bottom };
MapWindowPoints(hParent, HWND_DESKTOP, &topLeft, 1);
MapWindowPoints(hParent, HWND_DESKTOP, &bottomRight, 1);
control.rect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y };
}
else {
control.rect = localRect;
}
// Check if the control is enabled
control.isEnabled = IsWindowEnabled(hwnd);
// Check if the control is visible, considering parent visibility
control.isVisible = IsWindowVisible(hwnd);
HWND parent = GetParent(hwnd);
while (parent != NULL && control.isVisible) {
control.isVisible = control.isVisible && IsWindowVisible(parent);
parent = GetParent(parent);
}
// Add the control to the list
navigator->m_source.AddControl(control);
}
return TRUE; // Continue enumeration
}
void UIScannerNavigator::ScanWindow() {
m_source.ClearControls();
// Enumerate child windows and extract control information
EnumChildWindows(m_windowHandle, EnumChildProc, reinterpret_cast<LPARAM>(this));
}
} // namespace UIScanner
// --- START OF FILE UIScannerNavigatorSource.h ---
#pragma once
#include <vector>
#include <string>
#include <Windows.h>
namespace UIScanner {
struct ControlInfo {
std::wstring type; // e.g., "Button", "Label", "TextBox"
std::wstring text; // Text content of the control
RECT rect; // Position and size of the control
bool isEnabled; // Whether the control is enabled
bool isVisible; // Whether the control is visible
};
class UIScannerNavigatorSource {
public:
UIScannerNavigatorSource() = default;
~UIScannerNavigatorSource() = default;
void AddControl(const ControlInfo& control) {
m_controls.push_back(control);
}
const std::vector<ControlInfo>& GetControls() const {
return m_controls;
}
void ClearControls() {
m_controls.clear();
}
private:
std::vector<ControlInfo> m_controls;
};
} // namespace UIScanner
// --- START OF FILE UIScannerSpot.cpp ---
// UIScannerSpot.cpp
#include "UIScannerSpot.h"
#include <Psapi.h>
namespace UIScanner {
UIScannerSpot::UIScannerSpot() {}
void UIScannerSpot::Scan() {
m_windowInfoList.clear();
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(this));
}
std::vector<WindowInfo> UIScannerSpot::GetWindowInfoList() const {
return m_windowInfoList;
}
BOOL CALLBACK UIScannerSpot::EnumWindowsProc(HWND hwnd, LPARAM lParam) {
UIScannerSpot* scanner = reinterpret_cast<UIScannerSpot*>(lParam);
if (!scanner) {
return FALSE;
}
// Check ScanMode
switch (scanner->m_currentScanMode) {
case ScanMode::Raw:
// Process all windows in Raw mode
break;
case ScanMode::Visible:
if (!IsWindowVisible(hwnd)) {
return TRUE; // Skip non-visible windows
}
break;
case ScanMode::Active:
if (GetForegroundWindow() != hwnd) {
return TRUE; // Skip non-active windows
}
break;
case ScanMode::OnTop:
if (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) {
return TRUE; // Skip if not on top
}
break;
case ScanMode::Enabled:
if (!IsWindowEnabled(hwnd)) {
return TRUE; // Skip if not enabled
}
break;
}
// Check title requirement if needed
if (scanner->m_requireTitle && GetWindowTextLengthW(hwnd) == 0) {
return TRUE; // Skip windows without titles if required
}
WindowInfo info;
info.handle = hwnd;
// Get window title
int titleLength = GetWindowTextLengthW(hwnd);
if (titleLength > 0) {
info.title.resize(titleLength + 1);
GetWindowTextW(hwnd, &info.title[0], titleLength + 1);
info.title.resize(titleLength);
}
else {
info.title = L"N/A";
}
// Get window position and size
GetWindowRect(hwnd, &info.rect);
// Get window visibility
info.isVisible = IsWindowVisible(hwnd) != 0;
// Analyze the window
auto analysisResult = scanner->m_windowAnalyzer.GetWindowAnalysis(hwnd);
info.isAccessible = analysisResult.isAccessible;
info.isInteractive = analysisResult.isInteractive;
info.potentialIssues = analysisResult.potentialIssues;
// Get process information
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
info.processId = processId;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (hProcess != NULL) {
wchar_t processName[MAX_PATH];
if (GetModuleFileNameExW(hProcess, NULL, processName, MAX_PATH)) {
info.processName = processName;
}
else {
info.processName = L"N/A";
}
CloseHandle(hProcess);
}
else {
info.processName = L"N/A";
}
scanner->m_windowInfoList.push_back(info);
return TRUE;
}
} // namespace UIScanner
// --- START OF FILE UIScannerSpot.h ---
// UIScannerSpot.h
#pragma once
#include "SOURCE_UNITS_uiparsercore/WindowSource.h"
#include "SOURCE_UNITS_uiparsercore/WindowAnalyzer.h"
#include <vector>
#include <Windows.h>
namespace UIScanner {
enum class ScanMode {
Raw,
Visible,
Active,
OnTop,
Enabled
};
struct WindowInfo {
HWND handle = nullptr;
std::wstring title;
RECT rect = {}; // Initialize with an empty RECT
bool isVisible = false;
DWORD processId = 0;
std::wstring processName;
bool isAccessible = false;
bool isInteractive = false;
std::vector<std::wstring> potentialIssues;
};
class UIScannerSpot {
public:
UIScannerSpot();
~UIScannerSpot() = default;
void SetScanMode(ScanMode mode) { m_currentScanMode = mode; }
ScanMode GetScanMode() const { return m_currentScanMode; }
void SetRequireTitle(bool requireTitle) { m_requireTitle = requireTitle; }
bool GetRequireTitle() const { return m_requireTitle; }
void Scan();
std::vector<WindowInfo> GetWindowInfoList() const;
private:
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
std::vector<WindowInfo> m_windowInfoList;
WindowParser::WindowAnalyzer m_windowAnalyzer;
ScanMode m_currentScanMode = ScanMode::Raw;
bool m_requireTitle = false;
};
} // namespace UIScanner
// --- START OF FILE UIScannerSpotSource.h ---
#pragma once
#include <Windows.h>
#include <string>
namespace UIScanner {
struct WindowSpotInfo {
HWND handle;
std::wstring title;
};
} // namespace UIScanner
// --- START OF FILE WindowSource.h ---
#pragma once
#include "CommonTypes.h"
#include <memory>
namespace WindowParser {
class WindowSource {
public:
HWND GetWindowHandle() const;
std::wstring GetWindowTitle() const;
Position GetWindowPosition() const;
Size GetWindowSize() const;
WindowState GetWindowState() const;
static std::unique_ptr<WindowSource> FromHandle(HWND handle);
private:
HWND m_handle;
WindowSource(HWND handle) : m_handle(handle) {}
};
}
// --- START OF FILE ControlAnalyzer.h ---
#pragma once
#include "AnalysisResult.h"
#include <functional>
namespace WindowParser {
class ControlAnalyzer {
public:
using AnalysisCallback = std::function<void(const ControlAnalysisResult&)>;
ControlAnalyzer() = default;
~ControlAnalyzer() = default;
ControlAnalysisResult AnalyzeControlData(const ProcessingResult<ProcessedControlInfo>& processedData);
void SetCallback(AnalysisCallback callback) { m_callback = callback; }
private:
AnalysisCallback m_callback;
std::vector<std::wstring> CheckAccessibility(const ProcessedControlInfo& info);
};
}
// --- START OF FILE AnalysisResult.h ---
#pragma once
#include "ProcessorResult.h"
#include <vector>
#include <chrono>
namespace WindowParser {
struct WindowAnalysisResult {
bool isAccessible = false;
bool isInteractive = false;
std::vector<std::wstring> potentialIssues;
std::chrono::system_clock::time_point analysisTime;
ProcessedWindowInfo processedInfo;
};
struct ControlAnalysisResult {
bool isAccessible;
bool isInteractable;
std::vector<std::wstring> accessibilityIssues;
std::chrono::system_clock::time_point analysisTime;
ProcessedControlInfo processedInfo;
};
struct UIElementAnalysisResult {
bool isAccessible;
bool meetsAccessibilityStandards;
std::vector<std::wstring> recommendations;
std::chrono::system_clock::time_point analysisTime;
ProcessedUIElementInfo processedInfo;
};
}
// --- START OF FILE CommonTypes.h ---
#pragma once
#include <Windows.h>
#include <string>
namespace WindowParser {
struct Position {
int x;
int y;
int width;
int height;
};
struct Size {
int width;
int height;
};
enum class WindowState {
Normal,
Minimized,
Maximized
};
enum class ControlState {
Enabled,
Disabled,
Checked,
Unchecked
};
enum class UIElementState {
Visible,
Hidden
};
}
// --- START OF FILE ProcessorResult.h ---
#pragma once
#include <string>
#include <vector>
#include <memory>
#include "CommonTypes.h"
namespace WindowParser {
enum class ProcessingStatus {
Success,
Failed,
NoData,
InvalidHandle
};
template<typename T>
struct ProcessingResult;
struct ProcessedWindowInfo {
bool isResponding = false;
bool isVisible = false;
bool hasFocus = false;
std::wstring processName;
DWORD processId = 0;
};
struct ProcessedControlInfo {
bool isEnabled;
bool isVisible;
bool canInteract;
std::wstring className;
};
struct ProcessedUIElementInfo {
bool isVisible;
bool isEnabled;
std::wstring automationId;
};
template<typename T>
struct ProcessingResult {
ProcessingStatus status = ProcessingStatus::NoData; // Or another appropriate default
std::shared_ptr<T> data;
std::wstring message;
};
}
// --- START OF FILE WindowData.h ---
#pragma once
#include "CommonTypes.h"
#include <string>
namespace WindowParser {
class WindowData {
public:
WindowData(HWND handle, const std::wstring& title,
const Position& pos, const Size& size,
WindowState state)
: m_handle(handle)
, m_title(title)
, m_position(pos)
, m_size(size)
, m_state(state) {
}
HWND handle() const { return m_handle; }
const std::wstring& title() const { return m_title; }
const Position& position() const { return m_position; }
const Size& size() const { return m_size; }
WindowState state() const { return m_state; }
private:
HWND m_handle;
std::wstring m_title;
Position m_position;
Size m_size;
WindowState m_state;
};
}
// --- START OF FILE UIElementAnalyzer.h ---
#pragma once
#include "AnalysisResult.h"
#include <functional>
namespace WindowParser {
class UIElementAnalyzer {
public:
using AnalysisCallback = std::function<void(const UIElementAnalysisResult&)>;
UIElementAnalyzer() = default;
~UIElementAnalyzer() = default;
UIElementAnalysisResult AnalyzeUIElementData(const ProcessingResult<ProcessedUIElementInfo>& processedData);
void SetCallback(AnalysisCallback callback) { m_callback = callback; }
private:
AnalysisCallback m_callback;
std::vector<std::wstring> ValidateAccessibilityStandards(const ProcessedUIElementInfo& info);
};
}
// --- START OF FILE WindowAnalyzer.cpp ---
#include "WindowAnalyzer.h"
#include "WindowProcessor.h"
namespace WindowParser {
WindowAnalysisResult WindowAnalyzer::AnalyzeWindowData(
const ProcessingResult<ProcessedWindowInfo>& processedData) {
WindowAnalysisResult result;
result.analysisTime = std::chrono::system_clock::now();
if (processedData.status != ProcessingStatus::Success || !processedData.data) {
result.isAccessible = false;
result.isInteractive = false;
result.potentialIssues.push_back(L"Failed to process window data");
return result;
}
result.processedInfo = *processedData.data;
result.isInteractive = CheckInteractivity(result.processedInfo);
result.potentialIssues = AnalyzeAccessibility(result.processedInfo);
result.isAccessible = result.potentialIssues.empty();
if (m_callback) {
m_callback(result);
}
return result;
}
WindowAnalysisResult WindowAnalyzer::GetWindowAnalysis(HWND handle) {
WindowProcessor processor;
auto processedData = processor.GetProcessedWindowData(handle);
return AnalyzeWindowData(processedData);
}
std::vector<std::wstring> WindowAnalyzer::AnalyzeAccessibility(const ProcessedWindowInfo& info) {
std::vector<std::wstring> issues;
if (!info.isVisible) {
issues.push_back(L"Window is not visible");
}
if (!info.isResponding) {
issues.push_back(L"Window is not responding");
}
return issues;
}
bool WindowAnalyzer::CheckInteractivity(const ProcessedWindowInfo& info) {
return info.isResponding && info.isVisible;
}
}
// --- START OF FILE WindowProcessor.cpp ---
#include "WindowProcessor.h"
#include <psapi.h>
namespace WindowParser {
ProcessingResult<ProcessedWindowInfo> WindowProcessor::ProcessWindowData(const WindowData& windowData) {
ProcessingResult<ProcessedWindowInfo> result;
result.data = std::make_shared<ProcessedWindowInfo>();
if (!IsWindow(windowData.handle())) {
result.status = ProcessingStatus::InvalidHandle;
result.message = L"Invalid window handle";
return result;
}
// Get process information
std::wstring processName;
DWORD processId;
if (!GetWindowProcessInfo(windowData.handle(), processName, processId)) {
result.status = ProcessingStatus::Failed;
result.message = L"Failed to get process information";
return result;
}
// Fill processed info
result.data->isResponding = IsWindowEnabled(windowData.handle());
result.data->isVisible = IsWindowVisible(windowData.handle());
result.data->hasFocus = (GetForegroundWindow() == windowData.handle());
result.data->processName = processName;
result.data->processId = processId;
result.status = ProcessingStatus::Success;
if (m_callback) {
m_callback(result);
}
return result;
}
ProcessingResult<ProcessedWindowInfo> WindowProcessor::GetProcessedWindowData(HWND handle) {
auto windowSource = WindowSource::FromHandle(handle);
if (!windowSource) {
ProcessingResult<ProcessedWindowInfo> result;
result.status = ProcessingStatus::InvalidHandle;
result.message = L"Could not create WindowSource from handle";
return result;
}
WindowData data(
handle,
windowSource->GetWindowTitle(),
windowSource->GetWindowPosition(),
windowSource->GetWindowSize(),
windowSource->GetWindowState()
);
return ProcessWindowData(data);
}
bool WindowProcessor::GetWindowProcessInfo(HWND handle, std::wstring& processName, DWORD& processId) {
GetWindowThreadProcessId(handle, &processId);
if (processId == 0) return false;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (!hProcess) return false;
wchar_t buffer[MAX_PATH];
if (GetModuleFileNameEx(hProcess, nullptr, buffer, MAX_PATH)) {
processName = buffer;
}
CloseHandle(hProcess);
return true;
}
}
// --- START OF FILE WindowAnalyzer.h ---
#pragma once
#include "AnalysisResult.h"
#include <functional>
namespace WindowParser {
class WindowAnalyzer {
public:
using AnalysisCallback = std::function<void(const WindowAnalysisResult&)>;
WindowAnalyzer() = default;
~WindowAnalyzer() = default;
WindowAnalysisResult AnalyzeWindowData(const ProcessingResult<ProcessedWindowInfo>& processedData);
WindowAnalysisResult GetWindowAnalysis(HWND handle);
void SetCallback(AnalysisCallback callback) { m_callback = callback; }
private:
AnalysisCallback m_callback;
std::vector<std::wstring> AnalyzeAccessibility(const ProcessedWindowInfo& info);
bool CheckInteractivity(const ProcessedWindowInfo& info);
};
}
// --- START OF FILE WindowProcessor.h ---
#pragma once
#include "ProcessorResult.h"
#include "WindowData.h"
#include "WindowSource.h" // Add this include
#include <functional>
namespace WindowParser {
class WindowProcessor {
public:
using WindowCallback = std::function<void(const ProcessingResult<ProcessedWindowInfo>&)>;
WindowProcessor() = default;
~WindowProcessor() = default;
ProcessingResult<ProcessedWindowInfo> ProcessWindowData(const WindowData& windowData);
ProcessingResult<ProcessedWindowInfo> GetProcessedWindowData(HWND handle);
void SetCallback(WindowCallback callback) { m_callback = callback; }
private:
WindowCallback m_callback;
bool GetWindowProcessInfo(HWND handle, std::wstring& processName, DWORD& processId);
};
}
// --- START OF FILE WindowSource.cpp ---
#include "WindowSource.h"
namespace WindowParser {
HWND WindowSource::GetWindowHandle() const {
return m_handle;
}
std::wstring WindowSource::GetWindowTitle() const {
int length = GetWindowTextLengthW(m_handle);
if (length == 0) return std::wstring();
std::wstring title(length + 1, 0);
GetWindowTextW(m_handle, &title[0], length + 1);
title.resize(length);
return title;
}
Position WindowSource::GetWindowPosition() const {
RECT rect;
GetWindowRect(m_handle, &rect);
return { rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top };
}
Size WindowSource::GetWindowSize() const {
RECT rect;
GetWindowRect(m_handle, &rect);
return { rect.right - rect.left, rect.bottom - rect.top };
}
WindowState WindowSource::GetWindowState() const {
WINDOWPLACEMENT placement = { sizeof(WINDOWPLACEMENT) };
GetWindowPlacement(m_handle, &placement);
switch (placement.showCmd) {
case SW_SHOWMAXIMIZED:
return WindowState::Maximized;
case SW_SHOWMINIMIZED:
return WindowState::Minimized;
default:
return WindowState::Normal;
}
}
std::unique_ptr<WindowSource> WindowSource::FromHandle(HWND handle) {
if (!IsWindow(handle)) return nullptr;
return std::unique_ptr<WindowSource>(new WindowSource(handle));
}
}
// --- START OF FILE WPFElementHandle.h ---
#ifndef WPFELEMENTHANDLE_H
#define WPFELEMENTHANDLE_H
#include <string>
#include <Windows.h>
#include <UIAutomation.h>
#include <atlbase.h>
class WPFElementHandle {
protected:
CComPtr<IUIAutomationElement> element;
public:
WPFElementHandle(CComPtr<IUIAutomationElement> elem) : element(elem) {}
virtual ~WPFElementHandle() = default;
std::wstring GetName() const;
std::wstring GetClassName() const;
};
#endif // WPFELEMENTHANDLE_H
// --- START OF FILE WPFElementParser.h ---
#ifndef WPFELEMENTPARSER_H
#define WPFELEMENTPARSER_H
#include "WPFElementAttributes.h"
#include <vector>
class WPFElementParser {
public:
static std::vector<WPFElementAttributes> ParseChildElements(CComPtr<IUIAutomationElement> parent);
};
#endif // WPFELEMENTPARSER_H
// --- START OF FILE WPFElementAttributes.h ---
#ifndef WPFELEMENTATTRIBUTES_H
#define WPFELEMENTATTRIBUTES_H
#include "WPFElementHandle.h"
class WPFElementAttributes : public WPFElementHandle {
public:
WPFElementAttributes(CComPtr<IUIAutomationElement> elem) : WPFElementHandle(elem) {}
RECT GetBoundingRectangle() const;
bool IsEnabled() const;
bool IsVisible() const;
};
#endif // WPFELEMENTATTRIBUTES_H
// --- START OF FILE WindowParser.h ---
#ifndef WINDOWPARSER_H
#define WINDOWPARSER_H
#include "WindowAttributes.h"
#include <vector>
class WindowParser {
public:
static std::vector<HWND> EnumerateChildWindows(HWND parent);
static std::vector<WindowAttributes> ParseChildWindows(HWND parent);
};
#endif // WINDOWPARSER_H
// --- START OF FILE WindowHandle.h ---
#ifndef WINDOWHANDLE_H
#define WINDOWHANDLE_H
#include <windows.h>
#include <string>
class WindowHandle {
protected:
HWND hWnd;
public:
WindowHandle(HWND handle) : hWnd(handle) {}
virtual ~WindowHandle() = default;
HWND GetHandle() const { return hWnd; }
std::string GetWindowText() const;
std::string GetClassName() const;
};
#endif // WINDOWHANDLE_H
// --- START OF FILE WindowAttributes.h ---
#ifndef WINDOWATTRIBUTES_H
#define WINDOWATTRIBUTES_H
#include "WindowHandle.h"
class WindowAttributes : public WindowHandle {
public:
WindowAttributes(HWND handle) : WindowHandle(handle) {}
RECT GetDimensions() const;
bool IsVisible() const;
bool IsEnabled() const;
};
#endif // WINDOWATTRIBUTES_H
// --- START OF FILE ControlProcessor.h ---
#pragma once
#include <string>
#include <Windows.h>
#include "UIScannerNavigator.h"
namespace WindowParser {
class ControlProcessor {
public:
ControlProcessor() = default;
~ControlProcessor() = default;
UIScanner::ControlType DetermineControlType(HWND hwnd);
std::wstring GetControlText(HWND hwnd, UIScanner::ControlType type);
};
}
// --- START OF FILE ControlProcessor.cpp ---
#include "ControlProcessor.h"
namespace WindowParser {
UIScanner::ControlType ControlProcessor::DetermineControlType(HWND hwnd) {
wchar_t className[256];
GetClassName(hwnd, className, 256);
std::wstring classNameStr(className);
if (classNameStr == L"Button") {
return UIScanner::ControlType::Button;
}
else if (classNameStr == L"Edit") {
return UIScanner::ControlType::Edit;
}
else if (classNameStr == L"Static") {
return UIScanner::ControlType::Static;
}
else if (classNameStr == L"ComboBox") {
return UIScanner::ControlType::ComboBox;
}
else if (classNameStr == L"msctls_statusbar32") {
return UIScanner::ControlType::StatusBar;
}
else if (classNameStr == L"SysTreeView32") {
return UIScanner::ControlType::TreeView;
}
else if (classNameStr == L"ToolbarWindow32") {
return UIScanner::ControlType::Toolbar;
}
else {
return UIScanner::ControlType::Unknown;
}
}
std::wstring ControlProcessor::GetControlText(HWND hwnd, UIScanner::ControlType type) {
switch (type) {
case UIScanner::ControlType::Button:
case UIScanner::ControlType::Static:
case UIScanner::ControlType::Edit: {
int len = GetWindowTextLength(hwnd);
if (len > 0) {
std::wstring text;
text.resize(len + 1);
GetWindowText(hwnd, &text[0], len + 1);
text.resize(len);
return text;
}
break;
}
case UIScanner::ControlType::ComboBox: {
// Get the currently selected item text
LRESULT selIndex = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
if (selIndex != CB_ERR) {
LRESULT textLen = SendMessage(hwnd, CB_GETLBTEXTLEN, selIndex, 0);
if (textLen > 0) {
std::wstring text;
text.resize(textLen + 1);
SendMessage(hwnd, CB_GETLBTEXT, selIndex, (LPARAM)&text[0]);
text.resize(textLen);
return text;
}
}
break;
}
case UIScanner::ControlType::TreeView:
return L"<TreeView>";
case UIScanner::ControlType::Toolbar:
return L"<Toolbar>";
default:
break;
}
return L"";
}
}
There is a test example to obtain in the third test the data of the interface elements of the specific window `Fax e Scanner do Windows` (title in Portuguese).
However, the terminal output presented is this:
"""
--- All Windows (UIScannerSpot) ---
Handle Title Position Visible PID Process Name Accessible Interactive Issues
----------------------------------------------------------------------------------------------------------------------------------
00000000000100B4N/A (0, 824) - (1536, 864)Yes 8948 C:\Windows\explorer.exe Yes Yes
0000000000020780C:\agents\SUBMODULES_uipars...(179, 179) - (1074, 697)Yes 19488 C:\Program Files\Microsoft ...Yes Yes
0000000000010556N/A (0, 824) - (1536, 824)Yes 7496 C:\Program Files\Microsoft ...Yes Yes
0000000000010552N/A (1536, 0) - (1536, 824)Yes 7496 C:\Program Files\Microsoft ...Yes Yes
000000000001054CN/A (0, 0) - (0, 824) Yes 7496 C:\Program Files\Microsoft ...Yes Yes
0000000000010536win_APIUIPCORE (Running) - ...(-7, -7) - (1543, 831)Yes 7496 C:\Program Files\Microsoft ...Yes Yes
0000000000060770OT┴VIO GUEDES da GLOBO NEWS...(-7, -7) - (1543, 831)Yes 11756 C:\Program Files\Google\Chr...Yes Yes
0000000000020712N/A (0, 0) - (1536, 824)Yes 8948 C:\Windows\explorer.exe Yes Yes
000000000001070ECalculadora (0, 1) - (320, 543) Yes 3824 C:\Program Files\WindowsApp...Yes Yes
00000000000302E6Calculadora (724, 255) - (1058, 805)Yes 16640 C:\Windows\System32\Applica...Yes Yes
0000000000010666Fax e Scanner do Windows (313, 132) - (1465, 730)Yes 12632 C:\Windows\System32\WFS.exe Yes Yes
00000000000203C8win_APIUIPCORE___v0_m0_p19 (479, 234) - (1393, 824)Yes 8948 C:\Windows\explorer.exe Yes Yes
00000000000101AAN/A (15, 0) - (1536, 3) Yes 8948 C:\Windows\explorer.exe Yes Yes
00000000000101B6N/A (0, 0) - (0, 0) Yes 8948 C:\Windows\explorer.exe Yes Yes
0000000000010190N/A (0, 0) - (0, 0) Yes 8948 C:\Windows\explorer.exe Yes Yes
000000000001018EN/A (0, 0) - (0, 0) Yes 8948 C:\Windows\explorer.exe Yes Yes
000000000003001AMicrosoft Text Input Applic...(0, 0) - (1536, 864)Yes 9212 C:\Windows\SystemApps\Micro...Yes Yes
000000000001015EProgram Manager (0, 0) - (1536, 864)Yes 8948 C:\Windows\explorer.exe Yes Yes
--- Detailed Information for 'Fax e Scanner do Windows' (UIScannerExtractor) ---
Detailed Window Information:
Handle: 0000000000010666
Title: Fax e Scanner do Windows
Position: (313, 132) - (1465, 730)
Visible: Yes
PID: 12632
Process Name: C:\Windows\System32\WFS.exe
Accessible: Yes
Interactive: Yes
--- Controls in 'Fax e Scanner do Windows' Window ---
Handle: 140
Type: TreeView
Text: <TreeView>
Position: (320, 213) - (320, 213)
Enabled: Yes
Visible: No
----------------------------
Handle: 140
Type: Unknown
Text:
Position: (320, 213) - (533, 691)
Enabled: Yes
Visible: No
----------------------------
Handle: 142
Type: TreeView
Text: <TreeView>
Position: (322, 214) - (535, 649)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 142
Type: Button
Text: Fax
Position: (322, 656) - (533, 678)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 142
Type: Button
Text: Digitalizar
Position: (322, 678) - (533, 700)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 142
Type: Unknown
Text:
Position: (322, 655) - (535, 701)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 140
Type: Unknown
Text:
Position: (320, 213) - (536, 703)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 21a
Type: Unknown
Text:
Position: (538, 213) - (1476, 234)
Enabled: Yes
Visible: No
----------------------------
Handle: 21a
Type: Unknown
Text:
Position: (538, 213) - (1476, 343)
Enabled: Yes
Visible: No
----------------------------
Handle: 21c
Type: Unknown
Text:
Position: (540, 214) - (1456, 235)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 21a
Type: Unknown
Text:
Position: (538, 349) - (1457, 703)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 21a
Type: Unknown
Text:
Position: (538, 213) - (1457, 703)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 280
Type: Unknown
Text:
Position: (640, 395) - (1778, 885)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 142
Type: Toolbar
Text: <Toolbar>
Position: (322, 214) - (1458, 239)
Enabled: Yes
Visible: No
----------------------------
Handle: 140
Type: Toolbar
Text: <Toolbar>
Position: (320, 188) - (1456, 217)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 280
Type: Unknown
Text:
Position: (640, 364) - (1778, 394)
Enabled: Yes
Visible: Yes
----------------------------
Handle: 280
Type: StatusBar
Text:
Position: (640, 885) - (1778, 904)
Enabled: Yes
Visible: Yes
----------------------------
C:\agents\SUBMODULES_uiparsercore\win_APIUIPCORE___v0_m0_p19\x64\Debug\win_APIUIPCORE.exe (process 13992) exited with code 0 (0x0).
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .
"""
Note that some interface components of this `Windows Fax and Scanner` window are still missing such as `Text` information in the test 3 report in win_APIUIPCORE.cpp.
What I think it is:
Also note that in some interface elements some effort was made to obtain some text from UI elements without text, such as `<TreeView>`, for elements without text. However, you know that the `Fax e Scanner do Windows` window has more interface elements than the buttons identified by the algorithm so far, and it is very possible that the visible elements identified as `Unknown` are elements that were not identified. It is known that the `Fax e Scanner do Windows` window is an app also built in WPF (Windows Presentation Foundation), that is, there is an integration of Win32 interface elements with WPF. The Win32 elements were those that were able to obtain information such as [Handle: 142, Type: Button, Text: Scan], [Handle: 142, Type: Button, Text: Fax], which are window elements that exist in the lower left corner of the `Windows Fax and Scanner` window, they are buttons of a higher hierarchy, since they function as tabs that change the entire screen of this app, of course also within these modes `Fax` e `Digitalizar` existe no canto esquerdo superior um TreeView como um explorador das pastas dentro do objeto `Fax` ou `Digitalizar`. Você entende?
I suspect that WFS.exe (Fax e Scanner do Windows) has the graphical user interface developed in WPF too, which is a more modern framework/library than the original Win32.
The Windows Fax and Scan application (WFS.exe) is primarily developed using Win32 APIs along with components from the Windows Presentation Foundation (WPF). While specific details about the libraries used in WFS.exe are not publicly documented, it is known that WPF enables a more modern user interface compared to traditional Win32 applications. This combination allows WFS to leverage both legacy functionalities and modern UI capabilities, providing users with a seamless experience for faxing and scanning documents.
For these cases where an app can be developed using other frameworks as well, how would you handle this in the Delphi code as well?
After analyzing all this and reflecting, can you see where the error is? How can you fix it? Could you write/rewrite the files to fix this? How would you guarantee in justification that your fix will be effective in achieving the goal of displaying correct data of all window (Win32 and WPF) elements?