Window Visibility Issue Debugging

Creato il: 10 gennaio 2025

Risposto usando OpenAI 01 da OpenAI01

Domanda


---

Take a look at this C++ console application project made in Visual Studio below.

UIScannerExtractor.cpp:
```
// UIScannerExtractor.cpp
#include "UIScannerExtractor.h"
#include "UIScannerHandler.h"
#include <iostream>

namespace UIScanner {

// Extract detailed information about windows
void UIScannerExtractor::ExtractWindowDetails(ScanMode mode) {
m_extractorSource.ClearWindowInfoList();
m_currentScanMode = mode;


EnumCallbackData data = { this, m_requireTitle };

EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&data));
}

// Callback function for EnumWindows
BOOL CALLBACK UIScannerExtractor::EnumWindowsProc(HWND hwnd, LPARAM lParam) {
EnumCallbackData* data = reinterpret_cast<EnumCallbackData*>(lParam);
UIScannerExtractor* extractor = static_cast<UIScannerExtractor*>(data->objPtr);

if (extractor) {
// Check if the window has a title if required
if (data->requireTitle) {
int titleLength = GetWindowTextLengthW(hwnd);
if (titleLength == 0) {
return TRUE;
}
}

// Filter based on scan mode
bool shouldProcess = false;
switch (extractor->m_currentScanMode) {
case ScanMode::Raw:
shouldProcess = true;
break;
case ScanMode::Visible:
shouldProcess = IsWindowVisible(hwnd);
if (shouldProcess) {
std::wcout << L"Window is visible: " << hwnd << std::endl;
}
else {
std::wcout << L"Window is not visible: " << hwnd << std::endl;
}
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) {
WindowInfo info;
info.handle = hwnd;

// Get window title
int length = GetWindowTextLengthW(hwnd);
if (length > 0) {
std::wstring title(length + 1, 0);
GetWindowTextW(hwnd, &title[0], length + 1);
title.resize(length);
info.title = title;
}
else {
info.title = L"";
}

// Get window position and size
GetWindowRect(hwnd, &info.rect);

// Get window visibility
info.isVisible = IsWindowVisible(hwnd) != 0;

// Store the processed information
extractor->m_extractorSource.AddWindowInfo(info);
}
}
return TRUE;
}

}
```

UIScannerExtractor.h:
```
#pragma once

#include "UIScannerExtractorSource.h"
#include "UIScannerSpot.h"
#include <Windows.h>

namespace UIScanner {

class UIScannerExtractor {
public:
UIScannerExtractor() = default;
~UIScannerExtractor() = default;

// Extract detailed information about windows
void ExtractWindowDetails(ScanMode mode = ScanMode::Raw);

// Get the stored window information
const std::vector<WindowInfo>& GetWindowInfoList() const {
return m_extractorSource.GetWindowInfoList();
}

// 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:
UIScannerExtractorSource m_extractorSource;
ScanMode m_currentScanMode = ScanMode::Raw;
bool m_requireTitle = false;

// Callback function for EnumWindows
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
};

}
```

UIScannerExtractorSource.h:
```
#pragma once

#include <vector>
#include <Windows.h>
#include <string>

namespace UIScanner {

struct WindowInfo {
HWND handle; // Window handle
std::wstring title; // Window title
RECT rect; // Window position and size
bool isVisible; // Window visibility
};

class UIScannerExtractorSource {
public:
UIScannerExtractorSource() = default;
~UIScannerExtractorSource() = default;

// Add processed window information to the storage
void AddWindowInfo(const WindowInfo& info) {
m_windowInfoList.push_back(info);
}

// Get all stored window information
const std::vector<WindowInfo>& GetWindowInfoList() const {
return m_windowInfoList;
}

// Clear all stored window information
void ClearWindowInfoList() {
m_windowInfoList.clear();
}

private:
std::vector<WindowInfo> m_windowInfoList; // Stores processed window information
};

} // namespace UIScanner
```

UIScannerHandler.cpp:
```
// 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
```

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);
};

}
```

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
```

UIScannerSpot.cpp:
```
// UIScannerSpot.cpp
#include "UIScannerSpot.h"

namespace UIScanner {

UIScannerSpot::UIScannerSpot() {}

void UIScannerSpot::Scan(ScanMode mode) {
// Use the mode passed as a parameter, or fall back to the property
m_currentScanMode = mode;
m_windowAnalysisResults.clear();
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(this));
}

std::vector<WindowParser::WindowAnalysisResult> UIScannerSpot::GetWindowAnalysisResults() const {
return m_windowAnalysisResults;
}

BOOL CALLBACK UIScannerSpot::EnumWindowsProc(HWND hwnd, LPARAM lParam) {
UIScannerSpot* scanner = reinterpret_cast<UIScannerSpot*>(lParam);
if (!scanner) {
return FALSE; // Should not happen
}

// Check if the window has a title if required
if (scanner->m_requireTitle) {
int titleLength = GetWindowTextLengthW(hwnd);
if (titleLength == 0) {
return TRUE; // Skip this window because it doesn't have a title
}
}

bool processWindow = false;
switch (scanner->m_currentScanMode) {
case ScanMode::Raw:
processWindow = true;
break;
case ScanMode::Visible: // Renamed from VisibleOnly
processWindow = IsWindowVisible(hwnd);
break;
case ScanMode::Active: // Renamed from ActiveOnly
processWindow = (GetForegroundWindow() == hwnd);
break;
case ScanMode::OnTop: // Handle the new mode
processWindow = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
break;
case ScanMode::Enabled:
processWindow = IsWindowEnabled(hwnd) != 0;
break;
default:
// Handle potential future modes or errors
break;
}

if (processWindow) {
auto windowSource = WindowParser::WindowSource::FromHandle(hwnd);
if (windowSource) {
auto analysisResult = scanner->m_windowAnalyzer.GetWindowAnalysis(hwnd);
scanner->m_windowAnalysisResults.push_back(analysisResult);
}
}

return TRUE;
}

} // namespace UIScanner
```

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, // Renamed from VisibleOnly
Active, // Renamed from ActiveOnly
OnTop, // Added the new mode
Enabled // Added the new mode
};

class UIScannerSpot {
public:
UIScannerSpot();
~UIScannerSpot() = default;

// Property to set/get the scan mode
void SetScanMode(ScanMode mode) { m_currentScanMode = mode; }
ScanMode GetScanMode() const { return m_currentScanMode; }

// Scan function with optional mode parameter
void Scan(ScanMode mode = ScanMode::Raw); // Defaults to Raw if no mode is provided

std::vector<WindowParser::WindowAnalysisResult> GetWindowAnalysisResults() const;

// Property to set/get whether to consider only windows with titles
void SetRequireTitle(bool requireTitle) { m_requireTitle = requireTitle; }
bool GetRequireTitle() const { return m_requireTitle; }

private:
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
std::vector<WindowParser::WindowAnalysisResult> m_windowAnalysisResults;
WindowParser::WindowAnalyzer m_windowAnalyzer;
ScanMode m_currentScanMode = ScanMode::Raw; // Default mode
bool m_requireTitle = false; // Default to false (get all windows)
};

} // namespace UIScanner
```

UIScannerSpotSource.h:
```
#pragma once

#include <Windows.h>
#include <string>

namespace UIScanner {

struct WindowSpotInfo {
HWND handle;
std::wstring title;
};

} // namespace UIScanner
```


```
#include <iostream>
#include <vector>
#include <iomanip>
#include "UIScannerSpot.h"
#include "UIScannerHandler.h"
#include "UIScannerExtractor.h"

// Structure to hold unified window information
struct UnifiedWindowInfo {
HWND handle;
std::wstring title;
RECT position;
bool isVisible;
DWORD processId;
std::wstring processName;
bool isAccessible;
bool isInteractive;
std::vector<std::wstring> potentialIssues;
};

static void displayReport(const std::vector<UnifiedWindowInfo>& 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.position.left) + L", " + std::to_wstring(info.position.top) + L") - (" +
std::to_wstring(info.position.right) + L", " + std::to_wstring(info.position.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;
}
}
}

int main() {
// Create the objects
UIScanner::UIScannerSpot spot;
UIScanner::UIScannerHandler uiHandler;
UIScanner::UIScannerExtractor uiExtractor;

// Helper function to create the unified report
auto createUnifiedReport = [&](const std::vector<HWND>& windowHandles,
const std::vector<UIScanner::WindowInfo>& windowInfoList,
const std::vector<WindowParser::WindowAnalysisResult>& windowAnalysisResults,
UIScanner::ScanMode mode) { // Function remains the same
std::vector<UnifiedWindowInfo> unifiedReport;
for (const auto& handle : windowHandles) {
// Filter for active window if in Active mode
if (mode == UIScanner::ScanMode::Active && GetForegroundWindow() != handle) {
continue; // Skip this window
}

UnifiedWindowInfo unifiedInfo = {};
unifiedInfo.handle = handle;
for (const auto& info : windowInfoList) {
if (info.handle == handle) {
unifiedInfo.title = info.title;
unifiedInfo.position = info.rect;
unifiedInfo.isVisible = info.isVisible;
break;
}
}
for (const auto& result : windowAnalysisResults) {
if (result.processedInfo.processId != 0) {
DWORD processId = 0;
GetWindowThreadProcessId(handle, &processId);
if (result.processedInfo.processId == processId) {
unifiedInfo.processId = result.processedInfo.processId;
unifiedInfo.processName = result.processedInfo.processName;
unifiedInfo.isAccessible = result.isAccessible;
unifiedInfo.isInteractive = result.isInteractive;
unifiedInfo.potentialIssues = result.potentialIssues;
break;
}
}
}
unifiedReport.push_back(unifiedInfo);
}
return unifiedReport;
};

// --- Set the desired mode and properties ---
UIScanner::ScanMode currentMode = UIScanner::ScanMode::Visible; // Example: Visible mode
spot.SetScanMode(currentMode);
uiHandler.SetScanMode(currentMode);
uiExtractor.SetScanMode(currentMode);

// Require windows to have titles
spot.SetRequireTitle(true);
uiHandler.SetRequireTitle(true);
uiExtractor.SetRequireTitle(true);

// Perform the scan using the mode
spot.Scan();
uiHandler.ScanWindows();
uiExtractor.ExtractWindowDetails();

// Get the results and display the report
std::vector<WindowParser::WindowAnalysisResult> analysisResults = spot.GetWindowAnalysisResults();
const std::vector<HWND>& windowHandles = uiHandler.GetWindowHandles();
const std::vector<UIScanner::WindowInfo>& windowInfoList = uiExtractor.GetWindowInfoList();

// Pass the mode to createUnifiedReport
displayReport(createUnifiedReport(windowHandles, windowInfoList, analysisResults, currentMode));

return 0;
}
```

SOURCE_UNITS_uiparsercore\AnalysisResult.h:
```
#pragma once
#include "ProcessorResult.h"
#include <vector>
#include <chrono>

namespace WindowParser {
struct WindowAnalysisResult {
bool isAccessible;
bool isInteractive;
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;
};
}
```

SOURCE_UNITS_uiparsercore\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
};
}
```

SOURCE_UNITS_uiparsercore\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);
};
}
```

SOURCE_UNITS_uiparsercore\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 {
ProcessingStatus status;
std::shared_ptr<T> data;
std::wstring message;
};

struct ProcessedWindowInfo {
bool isResponding;
bool isVisible;
bool hasFocus;
std::wstring processName;
DWORD processId;
};

struct ProcessedControlInfo {
bool isEnabled;
bool isVisible;
bool canInteract;
std::wstring className;
};

struct ProcessedUIElementInfo {
bool isVisible;
bool isEnabled;
std::wstring automationId;
};
}
```

SOURCE_UNITS_uiparsercore\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);
};
}
```

SOURCE_UNITS_uiparsercore\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;
}
}
```

SOURCE_UNITS_uiparsercore\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);
};
}
```

SOURCE_UNITS_uiparsercore\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;
};
}
```

SOURCE_UNITS_uiparsercore\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;
}
}
```

SOURCE_UNITS_uiparsercore\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);
};
}
```

SOURCE_UNITS_uiparsercore\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));
}
}
```

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) {}
};
}
```

Do you understand this project? Do you know why each detail is where it is? Do you know the ultimate purpose of this project?

---

I use the `ISO C++17 Standard (/std:c++17)` standard.

I'm having some confusion with this project, because it seems that when I apply the `Visible` mode (to display only visible windows), the non-visible windows are also displayed. It seems that the `visible` mode has no effect and is no different from the `Raw` mode (`Raw` mode that should display all open windows regardless of whether they are visible, accessible, enabled, or not). However, the `visible` mode should only show visible windows.

Would there be some hard-to-detect side effect causing this?

If you were to professionally develop this same project, which is to be able to list all windows, also including additional modes to filter and display only active windows, only windows on top, only visible windows, etc., how would you do it? And knowing this, what was missing from my project so that it would work correctly according to the filtering modes?

---

I will show an example output, even using the `Visible` mode setting (to display only visible windows), `UIScanner::ScanMode currentMode = UIScanner::ScanMode::Visible;`, but which erroneously displays non-visible windows as well:
"""
Handle Title Position Visible PID Process Name Accessible Interactive Issues
----------------------------------------------------------------------------------------------------------------------------------
00000000000700A0Medidor de Bateria (0, 0) - (13, 13) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000002015ANetwork Flyout (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000016057EPicture-in-picture (1222, 640) - (1517, 806)No 7776 C:\Program Files\Google\Chr...No No Window is not visible
00000000000203CEMicrosoft Visual Studio (534, 370) - (1002, 501)No 17956 C:\Program Files\Microsoft ...No No Window is not visible
00000000000C0282CiceroUIWndFrame (160, 160) - (320, 320)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000001308F0C:\agents\SUBMODULES_uipars...(154, 154) - (1049, 672)Yes 20228 C:\Program Files\Microsoft ...Yes Yes
00000000000D0792win_APIUIPCORE___v0_m0_p14 ...(-7, -7) - (1543, 831)Yes 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000D070AGDI+ Window (VCTIP.EXE) (0, 0) - (1, 1) No 20840 C:\Program Files\Microsoft ...No No Window is not visible
00000000000C0800.NET-BroadcastEventWindow.4...(0, 0) - (0, 0) No 20840 C:\Program Files\Microsoft ...No No Window is not visible
0000000000070388PlacementTargetWindow (140, 171) - (142, 173)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000D0708Hidden Window (128, 128) - (1280, 726)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000E0986*Sem tÝtulo - Bloco de Nota...(127, 150) - (1167, 718)Yes 18000 C:\Windows\System32\notepad...Yes Yes
000000000007075Awin_APIUIPCORE___v0_m0_p13 (483, 122) - (1397, 712)Yes 4500 C:\Windows\explorer.exe No No Window is not visible
00000000000204A2Wildstylez - Encore | Drago...(-7, -7) - (1543, 831)Yes 7776 C:\Program Files\Google\Chr...No No Window is not visible
000000000007055AUntitled prompt | Google AI...(-7, -7) - (1543, 831)Yes 7776 C:\Program Files\Google\Chr...No No Window is not visible
0000000000260908WindowSource.h - Visual Stu...(-25600, -25600) - (-25441, -25573)Yes 12084 C:\Users\user\AppData\Local...Yes Yes
00000000000307B2DeepSeek - Into the Unknown (-7, -7) - (1543, 831)Yes 7776 C:\Program Files\Google\Chr...No No Window is not visible
000000000004039CSOURCE_UNITS_uiparsercore (619, 78) - (1533, 668)Yes 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020970*Sem tÝtulo - Bloco de Nota...(-25600, -25600) - (-25441, -25573)Yes 4588 C:\Windows\System32\notepad...Yes Yes
00000000000A0694DeepL Translate: The world'...(-7, -7) - (1543, 831)Yes 7776 C:\Program Files\Google\Chr...No No Window is not visible
00000000000402A2Hidden Window (51, 51) - (1203, 649)No 17956 C:\Program Files\Microsoft ...No No Window is not visible
00000000000203BC.NET-BroadcastEventWindow.1...(0, 0) - (0, 0) No 17956 C:\Program Files\Microsoft ...No No Window is not visible
00000000000302A0SystemResourceNotifyWindow (0, 0) - (0, 0) No 17956 C:\Program Files\Microsoft ...No No Window is not visible
00000000000B05D4MediaContextNotificationWin...(0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000D0254SystemResourceNotifyWindow (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000D08DCMediaContextNotificationWin...(0, 0) - (0, 0) No 17956 C:\Program Files\Microsoft ...No No Window is not visible
00000000000203C8GDI+ Window (devenv.exe) (0, 0) - (1, 1) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000B073CCiceroUIWndFrame (1199, 651) - (1199, 651)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000D0370Hidden Window (179, 179) - (1331, 777)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
0000000000040940Hidden Window (26, 26) - (1178, 624)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
0000000000090346Hidden Window (128, 128) - (1280, 726)No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000508E2SystemResourceNotifyWindow (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000B036ESystemResourceNotifyWindow (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000001101DCSystemResourceNotifyWindow (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000706F8MediaContextNotificationWin...(0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
000000000004076ASystemResourceNotifyWindow (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000507A2Visual Studio Application M...(0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
0000000000060776.NET-BroadcastEventWindow.4...(0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000407A0DDE Server Window (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000606FAProgresso (128, 128) - (1280, 726)No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000080718GDI+ Window (explorer.exe) (0, 0) - (1, 1) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000000C0746Window (0, 0) - (1920, 1030)No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000040582HiddenFaxWindow (102, 102) - (1254, 700)No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000110650BluetoothNotificationAreaIc...(0, 0) - (133, 38) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020156MS_WebcheckMonitor (77, 77) - (1229, 675)No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000010502SleepDetectorServiceWindowC...(0, 0) - (133, 38) No 1436 C:\Users\user\AppData\Local...No No Window is not visible
000000000002042EC:\Users\user\AppData\Local...(51, 51) - (946, 569)No 12792 C:\Users\user\AppData\Local...No No Window is not visible
00000000000103F0.NET-BroadcastEventWindow.4...(0, 0) - (0, 0) No 2176 C:\Program Files\WindowsApp...No No Window is not visible
00000000000303D8Intel« Graphics Command Cen...(156, 156) - (462, 315)No 2176 C:\Program Files\WindowsApp...No No Window is not visible
00000000000203ECGDI+ Window (IGCCTray.exe) (0, 0) - (1, 1) No 2176 C:\Program Files\WindowsApp...No No Window is not visible
00000000000301F4Calculadora (0, 1) - (320, 543) Yes 1944 C:\Program Files\WindowsApp...Yes Yes
0000000000020162Calculadora (213, 174) - (547, 724)Yes 12588 C:\Windows\System32\Applica...Yes Yes
0000000000010336WavesFXConnection_Capture (104, 104) - (1256, 697)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000030334ChangeNotify (0, 0) - (0, 0) No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000030332WavesFXConnection_Render (78, 78) - (1230, 671)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000040330ChangeNotify (0, 0) - (0, 0) No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000201E6RealtekAudioBackgroundProce...(0, 0) - (0, 0) No 11860 C:\Windows\System32\RtkAudU...No No Window is not visible
0000000000010324AutoSubtypeOnAudioSession (52, 52) - (1204, 645)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010320CPowerState (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000001031ECBatterySaveMode (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000001031CDynamicEndpointCreator (182, 182) - (1334, 775)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000001031ARealtek Jack Windows - 28 (156, 156) - (1308, 749)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010318Realtek Jack Windows - 27 (130, 130) - (1282, 723)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010316Realtek Jack Windows - 26 (104, 104) - (1256, 697)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010314Realtek Jack Windows - 25 (78, 78) - (1230, 671)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010312Realtek Jack Windows - 24 (52, 52) - (1204, 645)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010310Realtek Jack Windows - 23 (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000001030ERealtek Jack Windows - 22 (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000007030CRealtek Jack Windows - 21 (182, 182) - (1334, 775)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000201D2Realtek Jack Windows - 20 (156, 156) - (1308, 749)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020268Realtek Jack Windows - 19 (130, 130) - (1282, 723)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020218Realtek Jack Windows - 18 (104, 104) - (1256, 697)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020212Realtek Jack Windows - 17 (78, 78) - (1230, 671)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020210Realtek Jack Windows - 16 (52, 52) - (1204, 645)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000002020ERealtek Jack Windows - 15 (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
000000000002020CRealtek Jack Windows - 14 (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202ACRealtek Jack Windows - 13 (182, 182) - (1334, 775)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202C2Realtek Jack Windows - 12 (156, 156) - (1308, 749)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202CERealtek Jack Windows - 11 (130, 130) - (1282, 723)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202DERealtek Jack Windows - 10 (104, 104) - (1256, 697)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000030200Realtek Jack Windows - 9 (78, 78) - (1230, 671)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000301CCRealtek Jack Windows - 8 (52, 52) - (1204, 645)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000201E4Realtek Jack Windows - 7 (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000201E2Realtek Jack Windows - 6 (26, 26) - (1178, 619)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020302Realtek Jack Windows - 5 (182, 182) - (1334, 775)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000301CARealtek Jack Windows - 4 (156, 156) - (1308, 749)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020098Realtek Jack Windows - 3 (130, 130) - (1282, 723)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000302FERealtek Jack Windows - 2 (104, 104) - (1256, 697)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202CARealtek Jack Windows - 1 (78, 78) - (1230, 671)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000402E0Realtek Jack Windows - 0 (52, 52) - (1204, 645)No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202D4SecurityHealthSystray (0, 0) - (0, 0) No 10256 C:\Windows\System32\Securit...No No Window is not visible
0000000000010308NVIDIA NodeJS Share Window (0, 0) - (136, 128) No 9868 C:\Program Files (x86)\NVID...No No Window is not visible
00000000000602C6{5AEA657D-F3F5-4BD8-BFE9-A4...(0, 0) - (136, 128) No 9868 C:\Program Files (x86)\NVID...No No Window is not visible
0000000000030050NvSvc (182, 182) - (1334, 775)No 0 N/A No No
00000000000402F6UxdService (156, 156) - (1308, 749)No 0 N/A No No
000000000001011EWindows Push Notifications ...(0, 0) - (0, 0) No 7972 C:\Windows\System32\svchost...No No Window is not visible
0000000000010110BroadcastListenerWindow (0, 0) - (136, 39) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
000000000001010ABroadcastListenerWindow (0, 0) - (136, 39) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
0000000000010104BroadcastListenerWindow (0, 0) - (136, 39) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
0000000000010102BroadcastListenerWindow (0, 0) - (136, 39) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
00000000000100D6The Event Manager Dashboard (0, 0) - (462, 358) No 8844 C:\Windows\System32\DriverS...No No Window is not visible
000000000002002ETask Host Window (0, 0) - (133, 38) No 8232 C:\Windows\System32\taskhos...No No Window is not visible
0000000000020030Task Host Window (0, 0) - (133, 38) No 8204 C:\Windows\System32\taskhos...No No Window is not visible
0000000000020034NvContainerWindowClass00001...(0, 0) - (0, 0) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
000000000002003ARealtekAudioAdminBackground...(0, 0) - (0, 0) No 0 N/A No No
000000000001007ERxDiag Message Pump 3.1 Mar...(300, 300) - (680, 400)No 0 N/A No No
0000000000010056NvContainerWindowClass00000...(0, 0) - (0, 0) No 0 N/A No No
0000000000020052DWM Notification Window (-25600, -25600) - (-25441, -25573)No 0 N/A No No
000000000008034CDDE Server Window (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000000300E8Microsoft Text Input Applic...(0, 0) - (1536, 864)Yes 16328 C:\Windows\SystemApps\Micro...Yes Yes
00000000000404D2Tsarathustra no X: "Eric Sc...(-25600, -25600) - (-25441, -25573)Yes 1436 C:\Users\user\AppData\Local...No No Window is not visible
00000000003E07D4Chat01.ai: OpenAI 01 gratui...(-25600, -25600) - (-25441, -25573)Yes 7776 C:\Program Files\Google\Chr...No No Window is not visible
00000000000D0760Google Translate (-25600, -25600) - (-25441, -25573)Yes 7776 C:\Program Files\Google\Chr...No No Window is not visible
000000000002012AProgram Manager (0, 0) - (1536, 864)Yes 4500 C:\Windows\explorer.exe No No Window is not visible
00000000000401DADefault IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020128MSCTFIME UI (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000000201E0Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020146Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000004022EDefault IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000003099EMSCTFIME UI (0, 0) - (0, 0) No 9164 C:\Windows\System32\conhost...No No Window is not visible
Window is not responding
0000000000190600Default IME (0, 0) - (0, 0) No 9164 C:\Windows\System32\conhost...No No Window is not visible
Window is not responding
00000000000A0684MSCTFIME UI (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000A072EDefault IME (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000A0836Default IME (0, 0) - (0, 0) No 20840 C:\Program Files\Microsoft ...No No Window is not visible
00000000000B087CDefault IME (0, 0) - (0, 0) No 20840 C:\Program Files\Microsoft ...No No Window is not visible
00000000000E07BEDefault IME (0, 0) - (0, 0) No 0 N/A No No
00000000000C0820MSCTFIME UI (0, 0) - (0, 0) No 18000 C:\Windows\System32\notepad...Yes Yes
00000000000907D8Default IME (0, 0) - (0, 0) No 18000 C:\Windows\System32\notepad...Yes Yes
00000000000406EAMSCTFIME UI (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000001A06A0Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000002B065ADefault IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000001708C4MSCTFIME UI (0, 0) - (0, 0) No 12084 C:\Users\user\AppData\Local...Yes Yes
00000000004D0890Default IME (0, 0) - (0, 0) No 12084 C:\Users\user\AppData\Local...Yes Yes
0000000000040912MSCTFIME UI (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020272Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020424MSCTFIME UI (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000030392Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000005307E6Default IME (0, 0) - (0, 0) No 1604 C:\Users\user\AppData\Local...No No Window is not visible
Window is not responding
00000000004B098CDefault IME (0, 0) - (0, 0) No 21380 C:\Users\user\AppData\Local...No No Window is not visible
Window is not responding
0000000000020950MSCTFIME UI (0, 0) - (0, 0) No 4588 C:\Windows\System32\notepad...Yes Yes
00000000000308B4Default IME (0, 0) - (0, 0) No 4588 C:\Windows\System32\notepad...Yes Yes
00000000001B0398Default IME (0, 0) - (0, 0) No 18880 C:\Program Files (x86)\Micr...No No Window is not visible
Window is not responding
000000000010086CDefault IME (0, 0) - (0, 0) No 16292 C:\Program Files (x86)\Micr...No No Window is not visible
Window is not responding
00000000000203A0Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
00000000000203BEDefault IME (0, 0) - (0, 0) No 17956 C:\Program Files\Microsoft ...No No Window is not visible
0000000000120060Default IME (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000D03D4Default IME (0, 0) - (0, 0) No 17956 C:\Program Files\Microsoft ...No No Window is not visible
0000000000020384Default IME (0, 0) - (0, 0) No 22976 C:\Program Files\Microsoft ...No No Window is not visible
Window is not responding
00000000000406F4MSCTFIME UI (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000008071CDefault IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000060738Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000002014EDefault IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000050348Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000003001ADefault IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000030376Default IME (0, 0) - (0, 0) No 6908 C:\Program Files (x86)\Micr...No No Window is not visible
Window is not responding
000000000002035EDefault IME (0, 0) - (0, 0) No 7908 C:\Program Files (x86)\Micr...No No Window is not visible
Window is not responding
0000000000010522Default IME (0, 0) - (0, 0) No 6592 C:\Users\user\AppData\Local...No No Window is not visible
Window is not responding
0000000000020202Default IME (0, 0) - (0, 0) No 12464 C:\Program Files\Google\Chr...No No Window is not visible
Window is not responding
00000000000204CEDefault IME (0, 0) - (0, 0) No 13304 C:\Users\user\AppData\Local...No No Window is not visible
Window is not responding
00000000000403DCDefault IME (0, 0) - (0, 0) No 13256 C:\Program Files\Google\Chr...No No Window is not visible
Window is not responding
0000000000020430Default IME (0, 0) - (0, 0) No 12804 C:\Windows\System32\conhost...No No Window is not visible
Window is not responding
00000000000403E8Default IME (0, 0) - (0, 0) No 12728 C:\Users\user\AppData\Local...No No Window is not visible
Window is not responding
00000000000203E6Default IME (0, 0) - (0, 0) No 2176 C:\Program Files\WindowsApp...No No Window is not visible
00000000000203EEDefault IME (0, 0) - (0, 0) No 2176 C:\Program Files\WindowsApp...No No Window is not visible
0000000000020468Default IME (0, 0) - (0, 0) No 1944 C:\Program Files\WindowsApp...Yes Yes
00000000000B02AADefault IME (0, 0) - (0, 0) No 12588 C:\Windows\System32\Applica...Yes Yes
000000000001032ADefault IME (0, 0) - (0, 0) No 11860 C:\Windows\System32\RtkAudU...No No Window is not visible
00000000000202C8Default IME (0, 0) - (0, 0) No 10268 C:\Windows\System32\DriverS...No No Window is not visible
00000000000202D6Default IME (0, 0) - (0, 0) No 10256 C:\Windows\System32\Securit...No No Window is not visible
000000000001030ADefault IME (0, 0) - (0, 0) No 9868 C:\Program Files (x86)\NVID...No No Window is not visible
0000000000010306Default IME (0, 0) - (0, 0) No 9868 C:\Program Files (x86)\NVID...No No Window is not visible
0000000000010120Default IME (0, 0) - (0, 0) No 7972 C:\Windows\System32\svchost...No No Window is not visible
000000000001011AMSCTFIME UI (0, 0) - (0, 0) No 8844 C:\Windows\System32\DriverS...No No Window is not visible
0000000000010112Default IME (0, 0) - (0, 0) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
000000000001010CDefault IME (0, 0) - (0, 0) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
0000000000010108Default IME (0, 0) - (0, 0) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
0000000000010106Default IME (0, 0) - (0, 0) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
00000000000100D8Default IME (0, 0) - (0, 0) No 8844 C:\Windows\System32\DriverS...No No Window is not visible
0000000000020032Default IME (0, 0) - (0, 0) No 6512 C:\Program Files\NVIDIA Cor...No No Window is not visible
0000000000020038Default IME (0, 0) - (0, 0) No 0 N/A No No
000000000001007CDefault IME (0, 0) - (0, 0) No 0 N/A No No
0000000000060276MSCTFIME UI (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
0000000000020564Default IME (0, 0) - (0, 0) No 4500 C:\Windows\explorer.exe No No Window is not visible
000000000012067ADefault IME (0, 0) - (0, 0) No 10348 C:\Windows\System32\Runtime...No No Window is not visible
Window is not responding
0000000000010510MSCTFIME UI (0, 0) - (0, 0) No 1436 C:\Users\user\AppData\Local...No No Window is not visible
00000000000104DCDefault IME (0, 0) - (0, 0) No 1436 C:\Users\user\AppData\Local...No No Window is not visible
0000000000020290MSCTFIME UI (0, 0) - (0, 0) No 7776 C:\Program Files\Google\Chr...No No Window is not visible
000000000003004EDefault IME (0, 0) - (0, 0) No 7776 C:\Program Files\Google\Chr...No No Window is not visible
00000000000300F0Default IME (0, 0) - (0, 0) No 16328 C:\Windows\SystemApps\Micro...Yes Yes

C:\agents\SUBMODULES_uiparsercore\win_APIUIPCORE___v0_m0_p14\x64\Debug\win_APIUIPCORE___v0_m0_p14.exe (process 3272) 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 . . .
"""

---

Could it be that the algorithm for displaying the report is wrong, displaying text with `Window is not visible`?

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 only visible windows.

Pensiero

Taking a closer look

I’m examining the code to determine why not all visible windows are being filtered correctly. The issue might lie in the logic for filtering windows or a project error.

Mapping out the logic

The question arises why invisible windows are appearing in the list. This could result from enumeration extending beyond the current step.

Tracking windows

I'm thinking through the enumeration of all windows within the report function, noting how invisible ones should be skipped if mode == UIScanner::ScanMode::Visible.

Adjusting my approach

I’m digging into uiHandler.ScanWindows() in UIScannerHandler.cpp, which should index only visible windows. Ensuring the correct mode and function call is crucial to filter out invisible windows.

Clarifying visible windows

I’m noting that uiExtractor.ExtractWindowDetails() confirms windows are in windowInfoList if visible. This suggests passing non-visible windows might be skipped unintentionally.

Clarifying window visibility

I’m confirming that only visible windows are added by uiHandler, crucial for processing in createUnifiedReport, thereby ensuring a precise and relevant report.

Revisiting visibility logic

Thinking through IsWindowVisible, it might inaccurately return true for certain windows, like those off-screen, minimized, or with dimensions of 0x0, leading to potential misclassification.

Rechecking enumeration logic

I’m noting a potential mix-up in combining results from spot, handler, and extractor. Also, the project might lack a required #define for proper visibility enumeration.

Clarifying window status

OK, so the issue seems to be with IsWindowVisible, which might inaccurately label all windows as visible. This leads me to think the enumeration process needs a closer look.

Spotting enumeration issues

The function's output should pivot on IsWindowVisible. Given all windows print as visible, the system might incorrectly continue enumerating all windows, skipping over invisible ones.

Confirming enumeration logic

The code should ignore invisible windows, yet they appear in the final report. It seems the user might be using ExtractWindowDetails(ScanMode::Raw) or has a default set to raw mode.

Clarifying enumeration terms

I’m noting how IsWindowVisible() and physical visibility differ, especially regarding minimized or out-of-screen windows, which might cause some confusion about window status.

Taking a closer look

I’m examining the initializations of ScanMode and m_currentScanMode, ensuring cleanliness in ExtractWindowDetails and ScanWindows, and avoiding leftover data from previous runs to maintain accuracy.

Revisiting the code logic

Hmm, I’m thinking about why non-visible windows might appear in the final report. It seems createUnifiedReport needs to skip invisible windows when filtering by ScanMode::Visible.

Pulling together insights

I’m noticing that only visible windows are processed in UIScannerSpot. The final report should ideally show only "Yes" for visible windows when the scanning mode is set to Visible.

Revisiting window visibility

I’m thinking through if IsWindowVisible() labels windows as visible when they aren't or if leftover data or concurrency issues might be affecting the final report's accuracy.

Assessing the issue

I'm mapping out the enumeration process, confirming it's capturing both visible and invisible windows. Incorrect final report logic might be causing the disparity observed.

Crafting a solution

I’m thinking through how to ensure only visible windows are included by refining EnumWindowsProc to filter out invisible windows, and similarly for UIScannerExtractor and createUnifiedReport.

Refining the solution

I’m working through ensuring only top-level visible windows are included by checking WS_VISIBLE styles and using IsWindowVisible to filter out non-visible windows from the final vector.

Thought about mode filtering in window scanner por 42 segundos

Risposta

Below is a fairly detailed explanation of what is really going on and how to fix it, based on the code you have provided and the behavior you’re seeing.


The Core Problem

Even though you have code in your EnumWindowsProc callbacks (both in UIScannerHandler and UIScannerExtractor) that checks for IsWindowVisible(hwnd), you still end up seeing windows that are marked as “not visible” in your final console report.

Why is that happening?

  1. You are indeed enumerating all top-level windows via EnumWindows(...).

    • Remember, EnumWindows does not filter out windows that are off-screen, zero-sized, hidden by other means, etc.
    • Every valid top-level window handle will come through your callback.
  2. In your code, you print out

    if (shouldProcess) { std::wcout << L"Window is visible: " << hwnd << std::endl; } else { std::wcout << L"Window is not visible: " << hwnd << std::endl; }

    but then do:

    if (shouldProcess) { // Store window info in the vector AddWindowInfo(info); }

    so only if shouldProcess == true (meaning IsWindowVisible(hwnd) is true) are you adding that window to your extractor/handler vectors (m_windowInfoList or m_windowHandles).

  3. Despite that, the final console shows a huge list of windows that say “No” for Visible.

    This indicates that somewhere you’re still passing along or merging data for every window into your final report, even those that did not pass the IsWindowVisible(hwnd) check.

  4. The aggregator (createUnifiedReport) never skips windows except in the Active case.
    In your main() function you do this:

    for (const auto& handle : windowHandles) { // Filter for active window if in Active mode if (mode == UIScanner::ScanMode::Active && GetForegroundWindow() != handle) { continue; // skip } // ... unifiedReport.push_back(unifiedInfo); }

    Notice there is no check that says:

    if (mode == UIScanner::ScanMode::Visible && info.isVisible == false) { continue; // skip invisible windows }

    So if, for some reason, windowHandles or windowInfoList contain entries for windows that are actually invisible (or were “visible” in the WS_VISIBLE sense but are truly offscreen/minimized/whatever), they will still be “reported” in that final displayReport(...) call.

  5. Possible mismatch in which object (Spot, Handler, Extractor) is actually returning which set of windows.

    • UIScannerSpot is doing its own enumeration, but you do spot.Scan() only to get analysisResults.
    • UIScannerHandler is enumerating in ScanWindows(), storing the handles in m_handlerSource.
    • UIScannerExtractor is enumerating in ExtractWindowDetails(), storing more detailed info in m_extractorSource.

    Then you combine them in createUnifiedReport(windowHandles, windowInfoList, windowAnalysisResults, currentMode).
    If any one of those enumerations was done in the wrong mode, or if the logic that merges them inadvertently picks up old data, you’ll get “invisible” windows in your final list.

  6. Remember that IsWindowVisible in Win32 does not guarantee “truly hidden” vs “completely shown.”

    • If a window has the WS_VISIBLE style bit set, IsWindowVisible(hwnd) will return nonzero (true), even if it is entirely off-screen, has zero width/height, or is behind other windows.
    • Conversely, a window can lose that bit for many reasons. By the time you query it again, it could appear as “not visible” if another style changed or if the app is in some transitional state.
    • In your log, you might see a contradictory scenario: The code only adds the handle if IsWindowVisible == true, but later, by the time you do info.isVisible = IsWindowVisible(hwnd), it might return false.

    This can happen if the window changed between the enumeration callback and the time you read out the rectangle or final attributes. You end up with “No” for visible even though the enumeration said it was visible.


Where the Error Likely Is

The most common mistake (and the easiest to make) is in the final reporting logic. In your snippet:

std::vector<UnifiedWindowInfo> createUnifiedReport( const std::vector<HWND>& windowHandles, const std::vector<UIScanner::WindowInfo>& windowInfoList, const std::vector<WindowParser::WindowAnalysisResult>& windowAnalysisResults, UIScanner::ScanMode mode) { std::vector<UnifiedWindowInfo> unifiedReport; for (const auto& handle : windowHandles) { // THE ONLY FILTER: if (mode == UIScanner::ScanMode::Active && GetForegroundWindow() != handle) { continue; // skip everything else in Active mode } // Build 'UnifiedWindowInfo' object UnifiedWindowInfo unifiedInfo = {}; unifiedInfo.handle = handle; // Merge data from windowInfoList for (const auto& info : windowInfoList) { if (info.handle == handle) { unifiedInfo.title = info.title; unifiedInfo.position = info.rect; unifiedInfo.isVisible = info.isVisible; break; } } // Merge data from windowAnalysisResults for (const auto& result : windowAnalysisResults) { DWORD processId = 0; ::GetWindowThreadProcessId(handle, &processId); if (processId != 0 && result.processedInfo.processId == processId) { unifiedInfo.processId = result.processedInfo.processId; unifiedInfo.processName = result.processedInfo.processName; unifiedInfo.isAccessible = result.isAccessible; unifiedInfo.isInteractive = result.isInteractive; unifiedInfo.potentialIssues = result.potentialIssues; break; } } unifiedReport.push_back(unifiedInfo); } return unifiedReport; }
  • Nowhere do you say:

    if (mode == UIScanner::ScanMode::Visible && !unifiedInfo.isVisible) continue;

    So if, for any reason, handle ended up in windowHandles, or if isVisible changes between the moment the handle was added and the moment you do the final GetWindowText/IsWindowVisible check, you still list it.

  • Alternatively, if you used the same data structure for all windows (some of which might be from a previous scan or from a different place in code), you could be unintentionally merging them.

Hence, the enumerators look correct, but your aggregator and final display do not actively skip or remove the invisible windows.


How to Fix It

1) Make sure your enumerations truly skip non-visible windows

Double-check your UIScannerHandler::EnumWindowsProc and UIScannerExtractor::EnumWindowsProc:

case ScanMode::Visible: shouldProcess = ::IsWindowVisible(hwnd); // ... if (shouldProcess) { // push_back the handle (Handler) or WindowInfo (Extractor) }

That part is likely fine; you already have it.
But do confirm you do not call AddWindowHandle(...) or AddWindowInfo(...) in any other place.

2) Guarantee your aggregator only sees the windows you wanted

If you want a truly strict guarantee that the final output includes only the windows that are actually visible at reporting time, you have two main choices:

  • Choice A: “Offline” approach

    • Only gather the subset in your m_windowHandles or m_windowInfoList that pass the IsWindowVisible check.
    • Then your aggregator only iterates over that pre-filtered subset of HWNDs.
    • If you see windows that say “No” to visible, it is typically because IsWindowVisible(hwnd) changed in the time between enumeration and final printing. In that case, if you truly want to hide them from the final console, you do a second check at the aggregator stage and skip them if they have become invisible.
  • Choice B: “Strict real-time” approach in the aggregator

    • Even though you enumerated them (and stored them), do a final “only show me truly visible ones” check like:
      // If user asked for Visible mode, also enforce the final check: if (mode == UIScanner::ScanMode::Visible && !info.isVisible) { // skip it continue; }
    • This ensures that you never push them into the final vector, no matter what the enumeration said earlier.

3) Watch out for “Partially Visible” vs. “Completely Hidden”

A window can have the WS_VISIBLE style bit set but be drawn off-screen or behind others. Windows does not truly track whether it is “physically on top” in your monitor or “partially covered.” The simplest fix if you just want to skip all windows that do not have the WS_VISIBLE style is indeed IsWindowVisible(hwnd). But that’s often not the same as “the user can see it on their display.”

4) Confirm that you clear out old data

You do call ClearWindowInfoList() and ClearWindowHandles() at the start of each new scan. Make sure you aren’t mixing data from old scans or from different objects (e.g., spot vs handler vs extractor). A typical pattern is to use only one enumerator and unify the results, instead of enumerating 3 times in 3 different classes. But that is more of a design choice.


A More Professional Approach

If your ultimate goal is to produce a tool that:

  1. Lists top-level windows.
  2. Allows filtering by “Visible,” “Enabled,” “OnTop,” “Active,” etc.
  3. Collects extended info like title, rectangle, process ID, accessibility analysis, etc.

Then professionally, you’d probably:

  1. Write one single enumerator (or at least unify them behind a single function/class) that calls EnumWindows(...) or EnumChildWindows(...) as needed.
  2. For each enumerated window, gather all the data you need:
    • Title, rect, IsWindowVisible(), IsWindowEnabled(), GetWindowLong(hwnd, GWL_EXSTYLE), etc.
    • Possibly the _com_ptr or UIAutomation checks to see if it’s accessibility-enabled, etc.
  3. Store that data in a single vector of “WindowRecord” or “WindowInfo,” which is a superset of your various classes.
  4. Then have your user choose a “mode” or “filter.” You simply run something like:
    auto result = std::vector<WindowRecord>(); for (auto& w : allEnumeratedWindows) { if (mode == Visible && !w.isVisible) continue; if (mode == Enabled && !w.isEnabled) continue; if (mode == OnTop && !(w.exStyle & WS_EX_TOPMOST)) continue; if (mode == Active && ::GetForegroundWindow() != w.hwnd) continue; result.push_back(w); }
  5. Print or display the final result.

Also, for real-world usage, you might:

  • Check for the WS_CAPTION style or WS_SYSMENU to identify “real” top-level windows.
  • Exclude or ignore the “worker” or “invisible tool” windows (e.g., Program Manager, CiceroUIWndFrame, .NET-BroadcastEventWindow, etc.).

But that depends on your requirements.


The Short Answer

“Yes, there is indeed a side effect causing the project to list windows which are actually not visible.
You are enumerating all top-level windows but never removing/skipping them in your final report logic unless the mode is Active.
The solution is to enforce the filter either (A) strictly in your enumerators or (B) once again in the final aggregator.”

One minimal fix:

// In createUnifiedReport: for (const auto& handle : windowHandles) { if (mode == UIScanner::ScanMode::Active && GetForegroundWindow() != handle) { continue; } // Build your unifiedInfo ... // *** Force skip if we want only visible windows but find it's not visible: if (mode == UIScanner::ScanMode::Visible && !unifiedInfo.isVisible) { continue; } unifiedReport.push_back(unifiedInfo); }

With that change, you will see that non-visible windows do not show up in the final table (i.e., they do not even appear as lines that say “No” for visible).

And of course, double-check that you are not accidentally adding any window to m_handlerSource or m_extractorSource if IsWindowVisible(hwnd) was false. That part looks okay in your code, but be mindful that if one of the calls is still in “Raw” mode or if something is leftover from a prior run, you might end up with invisible windows anyway.


Why This Will Actually Solve the Problem

  • By filtering again in the final step, you guarantee that only those windows that pass your chosen “visibility” criterion will be displayed in your final console output.
  • Even if a window was visible at enumeration time (according to WS_VISIBLE) but changed afterward, the final check in the aggregator will skip it if it’s no longer visible. Conversely, if you want to show them anyway, you just keep them.
  • In professional code, unifying enumeration into one pass and storing exactly the subset we want is simpler to reason about. Right now you have three scanners (Spot, Handler, Extractor) each enumerating differently. If you unify that, you remove a lot of confusion.

Final Takeaways

  1. Enumeration vs. Display – You must consistently enforce your filter either in the enumeration callback(s) or again at display time (or both).
  2. IsWindowVisible is not a perfect measure of physical on-screen visibility, but it is the standard Win32 check for “the window has the WS_VISIBLE style bit.”
  3. If you truly do want to list only the windows that are in a normal, interactive, top-level state, you’ll also have to filter out things like “Ghost” windows or “Message-only” windows, or windows that live far off-screen, etc.
  4. In your aggregator code, you currently filter only for “Active,” but you do not skip windows for any other mode. That’s the simplest fix.

That is the precise reason you see “Window is not visible” in your final console: you are enumerating them for analysis but never actually discarding them if they fail the “Visible” requirement when you build the final list.

Condividi questa Q&A