feat(list_windows): UIA-first top-level window enumeration on Windows - #1542
Conversation
Adds `crate::uia::enumerate_top_level_windows()` — a UI Automation-based enumerator that walks `AutomationElement::RootElement.FindAll(Children, ...)` and yields one `WindowInfo` per visible top-level window. Each entry's `hwnd` is the element's `NativeWindowHandle`, so downstream code keyed on the (pid, HWND) tuple keeps working unchanged. Modern apps (WebView2 hosts, packaged-UWP frames, Electron apps that wrap their real surface inside a container HWND) often hide their visible window from `EnumWindows`; UIA surfaces them with the real title + bounds. This commit is purely additive — `list_windows` still uses `EnumWindows`. Wiring happens in the next commit. Implementation notes: - COM init: STA via `CoInitializeEx(COINIT_APARTMENTTHREADED)`, with `RPC_E_CHANGED_MODE` swallowed (harmless — COM is up either way). - `IUIAutomation` is cached for the process lifetime in a `OnceLock` (CoCreateInstance is mildly expensive and the interface is thread-safe). - Filters on `CurrentIsOffscreen == false` and non-empty `GetWindowTextW` to match the existing `EnumWindows + IsWindowVisible` filter intent. - Pid is resolved via `GetWindowThreadProcessId` (not UIA's ProcessId property) so the (hwnd, pid) tuple stays bit-identical to whatever the rest of the driver computes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`crate::win32::list_windows` now walks UI Automation first (`AutomationElement::RootElement.FindAll(TreeScope::Children, ...)`, filtered to `IsOffscreen == false` + non-empty `GetWindowTextW`), then takes the union with the classic `EnumWindows` walk, deduped by HWND. The `filter_pid` argument is applied to the merged list at the end. Why: modern apps (WebView2-hosted Notepad, packaged-UWP frames, some Electron containers) hide their visible window inside a host HWND that `EnumWindows` either misses or surfaces with the wrong title/bounds. UIA returns the real interactable window with its true title + bounds. The UWP-Notepad case in particular went from "empty list for that pid" to "returns the actual window record". Why the EnumWindows union is kept: UIA can miss specific console window types and some installer dialogs. Merging both lists is maximally robust at negligible cost (dedupe is HWND-keyed). All callers (`list_windows`, `get_window_state`, `launch_app`, etc.) pick up the improvement uniformly through the shared `crate::win32::list_windows` helper — no per-tool changes needed. The z_index comment in the `list_windows` tool is updated to reflect the new ordering source. PARITY.md gains a new "Enumeration source (Windows)" subsection describing the UIA-first behavior, and the existing off-screen-windows limitation note is updated to mention both filters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds a UI Automation–based top-level window enumerator, integrates its results with EnumWindows in list_windows (union + HWND dedupe), moves PID filtering to after the merge, and updates docs and z-index comments to describe the new ordering and on_screen_only behavior. ChangesWindows Dual-Source Window Enumeration
Sequence Diagram(s)sequenceDiagram
participant Caller
participant COM as COM / CoInitializeEx
participant UIA as IUIAutomation
participant UIATree as UIA Desktop Children
participant Win32 as Win32 APIs (GetWindowText, GetWindowThreadProcessId, GetWindowRect)
participant DWM as DWM (DwmGetWindowAttribute)
Caller->>COM: get_uia() (CoInitializeEx STA)
COM-->>Caller: cached IUIAutomation
Caller->>UIA: get desktop root & children
UIA-->>UIATree: list of elements
loop for each element
Caller->>UIA: element->get_NativeWindowHandle
UIA-->>Caller: HWND
Caller->>Win32: GetWindowThreadProcessId, GetWindowTextW
Win32-->>Caller: PID, title
Caller->>DWM: DwmGetWindowAttribute(bounds)
DWM-->>Caller: bounds or fallback
Caller-->>Caller: build WindowInfo (filter offscreen/minimized/title)
end
Caller->>Win32: EnumWindows collection
Win32-->>Caller: Vec WindowInfo
Caller-->>Caller: merge UIA + EnumWindows, dedupe by HWND, apply filter_pid, return Vec
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/cua-driver-rs/crates/platform-windows/src/uia/windows_enum.rs`:
- Around line 65-81: The code currently writes 0 into UIA_SINGLETON on
CoCreateInstance failure which permanently caches a failure; change the
initialization logic so UIA_SINGLETON is only written on success: update
UIA_SINGLETON to a cell that can remain uninitialized on failure (e.g.,
OnceLock/OnceCell holding usize or Option<usize> as appropriate), call
CoCreateInstance(CUIAutomation) and if it Err(e) simply log and return None
without storing 0 into UIA_SINGLETON, and only store the raw_ptr (from
inst.as_raw()) after a successful creation (remember to std::mem::forget(inst))
so future calls will retry activation until a success is observed.
- Around line 51-60: The current ensure_com_initialized() calls CoInitializeEx
on every enumeration and never balances with CoUninitialize, which violates COM
per-thread init semantics; update ensure_com_initialized to perform a one-time,
per-thread initialization using a thread-local flag (e.g. thread_local! static
DID_INIT: Cell<bool> or OnceCell) so CoInitializeEx is invoked only once per
thread and subsequent calls are no-ops, preserving the existing error handling
for RPC_E_CHANGED_MODE; locate ensure_com_initialized (and callers
enumerate_top_level_windows / get_uia) and replace the unconditional
CoInitializeEx call with a thread-local guard that runs CoInitializeEx only on
the first entry for that thread.
In `@libs/cua-driver-rs/crates/platform-windows/src/win32/windows.rs`:
- Around line 58-69: Currently you append all uia_windows first then
EnumWindows-only entries, which pushes fallback (EnumWindows-only) windows
behind UIA ones and skews z_index; instead preserve Win32 (EnumWindows) stacking
order by iterating win32_windows first to build merged and seen, then iterate
uia_windows and for each w: if its hwnd is unseen, append it, but if it's
already present in merged (seen contains w.hwnd) do not change its
position—locate the existing merged entry by hwnd and merge/overwrite UIA
metadata fields into that entry so UIA data is preferred while keeping Win32
stack order; use the existing variables/structures (win32_windows, uia_windows,
merged, seen, w.hwnd) and update fields rather than reordering entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fd37e0fa-c340-4b82-b610-26ebbfe2c19c
📒 Files selected for processing (5)
libs/cua-driver-rs/PARITY.mdlibs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rslibs/cua-driver-rs/crates/platform-windows/src/uia/mod.rslibs/cua-driver-rs/crates/platform-windows/src/uia/windows_enum.rslibs/cua-driver-rs/crates/platform-windows/src/win32/windows.rs
| // UIA first — preserves UIA's preferred ordering for modern apps. | ||
| for w in uia_windows { | ||
| if seen.insert(w.hwnd) { | ||
| merged.push(w); | ||
| } | ||
| } | ||
| // Then any EnumWindows entry whose HWND wasn't already covered. | ||
| for w in win32_windows { | ||
| if seen.insert(w.hwnd) { | ||
| merged.push(w); | ||
| } | ||
| } |
There was a problem hiding this comment.
Merge order can skew z_index for fallback windows.
Appending EnumWindows-only entries after all UIA entries forces those windows behind UIA windows in list order, so downstream z_index is incorrect for exactly the fallback cases this PR keeps.
🧭 Suggested merge strategy (preserve Win32 stack order, prefer UIA metadata)
- // UIA first — preserves UIA's preferred ordering for modern apps.
- for w in uia_windows {
- if seen.insert(w.hwnd) {
- merged.push(w);
- }
- }
- // Then any EnumWindows entry whose HWND wasn't already covered.
- for w in win32_windows {
- if seen.insert(w.hwnd) {
- merged.push(w);
- }
- }
+ use std::collections::HashMap;
+ let mut uia_by_hwnd: HashMap<u64, WindowInfo> =
+ uia_windows.into_iter().map(|w| (w.hwnd, w)).collect();
+
+ // Keep EnumWindows order as canonical for z-order semantics,
+ // but replace overlapping HWND payloads with UIA-enriched data.
+ for w in win32_windows {
+ let hwnd = w.hwnd;
+ let preferred = uia_by_hwnd.remove(&hwnd).unwrap_or(w);
+ if seen.insert(hwnd) {
+ merged.push(preferred);
+ }
+ }
+
+ // UIA-only HWNDs (not present in EnumWindows) are appended.
+ for (_hwnd, w) in uia_by_hwnd {
+ if seen.insert(w.hwnd) {
+ merged.push(w);
+ }
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-rs/crates/platform-windows/src/win32/windows.rs` around lines
58 - 69, Currently you append all uia_windows first then EnumWindows-only
entries, which pushes fallback (EnumWindows-only) windows behind UIA ones and
skews z_index; instead preserve Win32 (EnumWindows) stacking order by iterating
win32_windows first to build merged and seen, then iterate uia_windows and for
each w: if its hwnd is unseen, append it, but if it's already present in merged
(seen contains w.hwnd) do not change its position—locate the existing merged
entry by hwnd and merge/overwrite UIA metadata fields into that entry so UIA
data is preferred while keeping Win32 stack order; use the existing
variables/structures (win32_windows, uia_windows, merged, seen, w.hwnd) and
update fields rather than reordering entries.
… cache) Replaces the process-wide OnceLock<usize> singleton with a thread_local RefCell<Option<IUIAutomation>>. The previous design had two issues: 1. A failed CoCreateInstance wrote a sentinel (0) into the OnceLock, permanently caching the failure: every subsequent call short-circuited to None and never retried. 2. ensure_com_initialized() called CoInitializeEx unconditionally on every enumeration with no balancing CoUninitialize, ignoring the per-thread init-once semantics COM expects. IUIAutomation objects are COM-apartment-scoped, so a thread-local naturally matches their lifetime: each spawn_blocking worker initializes COM as STA exactly once, builds its own IUIAutomation, and reuses it. On error the cell stays None so the next call retries from scratch — no sticky failure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…merge EnumWindows iterates the Win32 window manager's z-order list top-to-bottom, so its iteration order IS the actual z-order — making the merged array's index a meaningful z_index for any HWND the Win32 path saw. UIA's FindAll(TreeScope::Children, ...) makes no z-order guarantee, so listing UIA first (as before) skewed the index for windows present in both sources and demoted Win32-only entries to the tail despite their canonical position. Reorder the merge: EnumWindows entries first (canonical z-order), then UIA-only HWNDs appended after (extra coverage for modern containers EnumWindows misses, no claim on z-order priority). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Pushed fixup commits addressing the three review findings:
Finding 1 ( Finding 3 (z-order skewed by union order) — addressed in
@coderabbitai please re-review. |
🧩 Analysis chainI'll pull the latest state of the modified files to verify the fixes before re-reviewing. 🏁 Script executed: Length of output: 27790
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs`:
- Around line 268-272: Update the explanatory comment for z_index in impl_.rs to
describe the current merge order used by list_windows: EnumWindows order is
preserved first and UIA-only entries are appended afterward, so the inversion
`(len - 1 - i)` is applied so higher z_index corresponds to closer-to-front
windows; reference list_windows, z_index, EnumWindows and UIA in the comment to
make the rationale accurate.
In `@libs/cua-driver-rs/PARITY.md`:
- Around line 365-390: The documentation incorrectly states that
crate::win32::list_windows runs UIA-first; update the PARITY.md text to reflect
the actual behavior: EnumWindows is the canonical ordering and UIA contributes
only missing HWNDs after the EnumWindows walk (i.e., change "UIA-first" wording
to "EnumWindows-first" and rephrase the paragraph that explains ordering,
deduping by HWND, and why UIA is used only to add missing windows so it no
longer implies UIA determines z-order or parity expectations).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a902751a-94d9-4ac7-b5d2-73f02bbc7cc8
📒 Files selected for processing (5)
libs/cua-driver-rs/PARITY.mdlibs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rslibs/cua-driver-rs/crates/platform-windows/src/uia/mod.rslibs/cua-driver-rs/crates/platform-windows/src/uia/windows_enum.rslibs/cua-driver-rs/crates/platform-windows/src/win32/windows.rs
| // z_index: the underlying enumerator returns UIA-first (modern | ||
| // top-to-bottom) then any EnumWindows entries UIA missed (also | ||
| // roughly top-to-bottom). Higher list index = farther from front. | ||
| // Swift convention: higher z_index = closer to front. Invert via | ||
| // `(len - 1 - i)` so the front-most window gets the largest z. |
There was a problem hiding this comment.
Correct z_index comment to match current merge order.
The note still describes UIA-first ordering, but list_windows now preserves EnumWindows order first and appends UIA-only entries. The explanation should reflect that to keep z-index rationale accurate.
Suggested comment update
- // z_index: the underlying enumerator returns UIA-first (modern
- // top-to-bottom) then any EnumWindows entries UIA missed (also
- // roughly top-to-bottom). Higher list index = farther from front.
+ // z_index: the underlying enumerator returns EnumWindows first
+ // (canonical top-to-bottom z-order), then appends UIA-only HWNDs
+ // that EnumWindows missed. Higher list index = farther from front.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs` around lines
268 - 272, Update the explanatory comment for z_index in impl_.rs to describe
the current merge order used by list_windows: EnumWindows order is preserved
first and UIA-only entries are appended afterward, so the inversion `(len - 1 -
i)` is applied so higher z_index corresponds to closer-to-front windows;
reference list_windows, z_index, EnumWindows and UIA in the comment to make the
rationale accurate.
| - windows: VERIFIED (UIA-first enumeration; EnumWindows kept as union member) | ||
| - macOS: OPEN (audit pending — macOS port already exists) | ||
| - linux: OPEN | ||
| - Test: `crates/platform-windows/examples/list_windows_parity.rs` | ||
|
|
||
| ### Enumeration source (Windows) | ||
|
|
||
| `crate::win32::list_windows` runs UI Automation first | ||
| (`AutomationElement::RootElement.FindAll(TreeScope::Children, TrueCondition)`, | ||
| filtered to `IsOffscreen == false` + non-empty `GetWindowTextW`) and then | ||
| takes the union with the classic `EnumWindows` walk, deduped by HWND. Each | ||
| UIA element contributes its `NativeWindowHandle` as the canonical HWND, so | ||
| downstream code keyed on `(pid, window_id)` keeps working unchanged. | ||
|
|
||
| Why UIA-first: modern apps (WebView2-hosted Notepad, packaged-UWP frames, | ||
| some Electron containers) hide their visible window inside a host HWND that | ||
| `EnumWindows` either misses or surfaces with the wrong title/bounds. UIA's | ||
| desktop-children walk returns the real interactable window. | ||
|
|
||
| Why the EnumWindows union is kept: UIA can miss specific console window | ||
| types and some installer dialogs. Merging both lists is maximally robust at | ||
| negligible cost. | ||
|
|
||
| `filter_pid` is applied to the merged list, so a UWP app's pid that | ||
| previously returned empty now returns its real window. | ||
|
|
There was a problem hiding this comment.
Fix stale ordering docs (UIA-first → EnumWindows-first).
This section now contradicts the actual merge behavior: EnumWindows order is canonical, and UIA contributes only missing HWNDs after that. Please update this wording to avoid incorrect z-order/parity expectations.
Suggested doc patch
- - windows: VERIFIED (UIA-first enumeration; EnumWindows kept as union member)
+ - windows: VERIFIED (EnumWindows-first enumeration; UIA kept as union member for missed HWNDs)
-`crate::win32::list_windows` runs UI Automation first
-(`AutomationElement::RootElement.FindAll(TreeScope::Children, TrueCondition)`,
-filtered to `IsOffscreen == false` + non-empty `GetWindowTextW`) and then
-takes the union with the classic `EnumWindows` walk, deduped by HWND.
+`crate::win32::list_windows` runs `EnumWindows` first (canonical z-order),
+then appends UIA-only HWNDs from
+`AutomationElement::RootElement.FindAll(TreeScope::Children, TrueCondition)`
+(filtered to `IsOffscreen == false` + non-empty `GetWindowTextW`), deduped by HWND.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - windows: VERIFIED (UIA-first enumeration; EnumWindows kept as union member) | |
| - macOS: OPEN (audit pending — macOS port already exists) | |
| - linux: OPEN | |
| - Test: `crates/platform-windows/examples/list_windows_parity.rs` | |
| ### Enumeration source (Windows) | |
| `crate::win32::list_windows` runs UI Automation first | |
| (`AutomationElement::RootElement.FindAll(TreeScope::Children, TrueCondition)`, | |
| filtered to `IsOffscreen == false` + non-empty `GetWindowTextW`) and then | |
| takes the union with the classic `EnumWindows` walk, deduped by HWND. Each | |
| UIA element contributes its `NativeWindowHandle` as the canonical HWND, so | |
| downstream code keyed on `(pid, window_id)` keeps working unchanged. | |
| Why UIA-first: modern apps (WebView2-hosted Notepad, packaged-UWP frames, | |
| some Electron containers) hide their visible window inside a host HWND that | |
| `EnumWindows` either misses or surfaces with the wrong title/bounds. UIA's | |
| desktop-children walk returns the real interactable window. | |
| Why the EnumWindows union is kept: UIA can miss specific console window | |
| types and some installer dialogs. Merging both lists is maximally robust at | |
| negligible cost. | |
| `filter_pid` is applied to the merged list, so a UWP app's pid that | |
| previously returned empty now returns its real window. | |
| - windows: VERIFIED (EnumWindows-first enumeration; UIA kept as union member for missed HWNDs) | |
| - macOS: OPEN (audit pending — macOS port already exists) | |
| - linux: OPEN | |
| - Test: `crates/platform-windows/examples/list_windows_parity.rs` | |
| ### Enumeration source (Windows) | |
| `crate::win32::list_windows` runs `EnumWindows` first (canonical z-order), | |
| then appends UIA-only HWNDs from | |
| `AutomationElement::RootElement.FindAll(TreeScope::Children, TrueCondition)` | |
| (filtered to `IsOffscreen == false` + non-empty `GetWindowTextW`), deduped by HWND. Each | |
| UIA element contributes its `NativeWindowHandle` as the canonical HWND, so | |
| downstream code keyed on `(pid, window_id)` keeps working unchanged. | |
| Why UIA-first: modern apps (WebView2-hosted Notepad, packaged-UWP frames, | |
| some Electron containers) hide their visible window inside a host HWND that | |
| `EnumWindows` either misses or surfaces with the wrong title/bounds. UIA's | |
| desktop-children walk returns the real interactable window. | |
| Why the EnumWindows union is kept: UIA can miss specific console window | |
| types and some installer dialogs. Merging both lists is maximally robust at | |
| negligible cost. | |
| `filter_pid` is applied to the merged list, so a UWP app's pid that | |
| previously returned empty now returns its real window. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-rs/PARITY.md` around lines 365 - 390, The documentation
incorrectly states that crate::win32::list_windows runs UIA-first; update the
PARITY.md text to reflect the actual behavior: EnumWindows is the canonical
ordering and UIA contributes only missing HWNDs after the EnumWindows walk
(i.e., change "UIA-first" wording to "EnumWindows-first" and rephrase the
paragraph that explains ordering, deduping by HWND, and why UIA is used only to
add missing windows so it no longer implies UIA determines z-order or parity
expectations).
|
Pushed fixup for the 2 doc-drift nits: z_index comment in impl_.rs + PARITY.md "Enumeration source" section now correctly say EnumWindows-first / UIA-as-union-only. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 104192
This is consistent with the actual merge logic in
The "Why All findings across both rounds of review are resolved. The implementation is correct and the documentation now faithfully describes it. 🐇 [approve] |
Summary
crate::win32::list_windowson Windows now walks UI Automation first and unions with the classicEnumWindowsresult (deduped by HWND), so the visible top-level windows of modern apps stop disappearing.EnumWindowseither misses or returns with a misleading title/bounds. Askinglist_windowsfor such a pid was returning an empty array. UIA'sAutomationElement::RootElement.FindAll(TreeScope::Children, TrueCondition)returns the real interactable window — withNativeWindowHandlegiving back an honest Win32 HWND so the (pid, window_id) tuple stays compatible with the rest of the pipeline.EnumWindows: UIA can miss specific console window types and some installer dialogs. Merging both lists (HWND-keyed dedupe) is maximally robust at negligible cost.CurrentIsOffscreen == false+ non-emptyGetWindowTextW— same intent as the EnumWindows path'sIsWindowVisible && !IsIconic+ non-empty title.Implementation
Two commits:
feat(platform-windows): UIA tree walker helper for top-level windows— addscrates/platform-windows/src/uia/windows_enum.rswithenumerate_top_level_windows() -> Vec<WindowInfo>. Purely additive — no wiring yet. Stashes theIUIAutomationinstance in a process-lifetimeOnceLock(CoCreateInstance is non-trivial). Initializes COM viaCoInitializeEx(COINIT_APARTMENTTHREADED)on each call, swallowingRPC_E_CHANGED_MODE(means COM is already up in another mode — harmless).feat(list_windows): use UIA-first enumeration on Windows—crate::win32::list_windowsnow calls the helper, then unions withEnumWindows(dedupe by HWND), then applies the existingfilter_pidargument at the end. All callers (list_windowstool,get_window_state,launch_app, etc.) pick the improvement up uniformly because they all go through this single helper. Adds a new "Enumeration source (Windows)" subsection to PARITY.md and updates the existing off-screen-windows limitation note.Notes
Win32_UI_AccessibilityandWin32_System_Comwere already enabled inplatform-windows/Cargo.toml.GetWindowThreadProcessId(not UIA'sProcessIdproperty) so the (hwnd, pid) tuple stays bit-identical to what the EnumWindows path computes — important for the downstreamwindows_for_pid.iter().any(|w| w.hwnd == hwnd)invariants.GetWindowTextWrather than UIA'sCurrentNamefor the same parity reason (UIA's Name property occasionally returns the AX-friendly label rather than the OS-level window caption).Test plan
start ms-launch:Microsoft.WindowsNotepad_8wekyb3d8bbwe!App), grab its pid vialist_apps, thencua-driver call list_windows '{"pid":<UWP_pid>}'— pre-change returned empty; should now return a non-emptywindowsarray with the real window record.mspaintandregedit(classic Win32 apps); confirmlist_windowsstill returns their windows with identical bounds + titles to pre-change.cargo run -p platform-windows --example list_windows_parityagainst a running daemon — header format, structured-content field shape, and pid-warning path should all still pass.cargo build --release --target x86_64-pc-windows-msvc -p platform-windows— clean (verified locally; full workspace cross-build from macOS fails on an unrelatedringbuild-script that needs the Windows native toolchain, butplatform-windowsitself compiles cleanly).🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Documentation