fix(gui): window.list 进程名错乱 + screen.find UIA 只支持精确匹配
## bug 1:所有窗口的进程名都报成 powershell 根因:C# 回调里用 $pid 接 GetWindowThreadProcessId 的输出 —— 但 $pid 是 PowerShell **自动变量**(当前进程 ID),赋值被静默忽略,于是每个窗口都取到 powershell 的 PID。 改为 $procId。真机验证:cmd / hexin(同花顺) / SearchPictureTool / TextInputHost / explorer 均正确归属。 ## bug 2:UIA 路径实际是精确匹配,与文档「子串匹配」不符 根因:用 PropertyCondition(NameProperty, text, IgnoreCase) —— IgnoreCase 只影响大小写, 仍是精确相等。实测「管理员: C:\WINDOWS\system32\cmd.exe」用 '管理员' 永远匹配不到。 改为:FindAll(Descendants, [控制类型条件]) 后按 IndexOf(OrdinalIgnoreCase) 子串过滤, 并加 20000 元素扫描上限防止超大 UI 树卡住。 真机验证: '管理员' -> 命中 '管理员: C:\WINDOWS\system32\cmd.exe' @ (548,312) '同花顺' -> 命中窗口标题 + 内部文本元素 ## 顺带明确一个语义(写入文档) is_foreground = 「持有键盘焦点」,不等于「视觉上在最上层」。 实测 cmd 持有焦点(is_foreground=true)但画面被全屏的「同花顺」覆盖 —— 此时按 UIA 返回的坐标点击会打到覆盖它的窗口上。 因此:**几何信息可信,视觉遮挡需另行确认**(点击前截屏复核)。 单测 61/61、e2e 21/21 全绿
This commit is contained in:
1 parent
2dccec9a5c
commit
7efe717cf8
1 file changed
+20
-12
@@ -87,12 +87,14 @@ $cb = [NAWin32+EnumProc]{
|
||||
if (-not [NAWin32]::GetWindowRect($h, [ref]$r)) { return $true }
|
||||
$w = $r.Right - $r.Left; $ht = $r.Bottom - $r.Top
|
||||
if ($w -le 0 -or $ht -le 0) { return $true }
|
||||
$pid = 0
|
||||
[void][NAWin32]::GetWindowThreadProcessId($h, [ref]$pid)
|
||||
# ⚠️ 变量名不能叫 $pid —— 它是 PowerShell 的自动变量(当前进程 ID),赋值会被忽略,
|
||||
# 导致所有窗口都报成 powershell 进程(真机踩过)。
|
||||
$procId = 0
|
||||
[void][NAWin32]::GetWindowThreadProcessId($h, [ref]$procId)
|
||||
$pn = ''
|
||||
try { $pn = (Get-Process -Id $pid -ErrorAction Stop).ProcessName } catch {}
|
||||
try { $pn = (Get-Process -Id $procId -ErrorAction Stop).ProcessName } catch {}
|
||||
[void]$list.Add([pscustomobject]@{
|
||||
hwnd = ('0x{0:X}' -f $h.ToInt64()); title = $title; process = $pn; pid = $pid
|
||||
hwnd = ('0x{0:X}' -f $h.ToInt64()); title = $title; process = $pn; pid = $procId
|
||||
x = $r.Left; y = $r.Top; width = $w; height = $ht
|
||||
is_foreground = ($h -eq $fg); is_minimized = [NAWin32]::IsIconic($h)
|
||||
})
|
||||
@@ -260,28 +262,34 @@ if ($winTitle -ne '') {
|
||||
if ($w -eq $null) { Write-Output '[]'; exit 0 }
|
||||
$scope = $w
|
||||
}
|
||||
$nameCond = New-Object System.Windows.Automation.PropertyCondition(
|
||||
[System.Windows.Automation.AutomationElement]::NameProperty, $text,
|
||||
[System.Windows.Automation.PropertyConditionFlags]::IgnoreCase)
|
||||
// ⚠️ UIA 的 PropertyCondition 是**精确匹配**(IgnoreCase 只影响大小写),
|
||||
// 实测「管理员: C:\WINDOWS\...」这类标题用 "管理员" 永远匹配不到。
|
||||
// 故改为遍历后按子串过滤(并限制遍历规模,避免超大 UI 树卡住)。
|
||||
$cond = [System.Windows.Automation.Condition]::TrueCondition
|
||||
$ct = ${JSON.stringify(ct)}
|
||||
if ($ct -ne '') {
|
||||
$ctObj = $null
|
||||
try { $ctObj = [System.Windows.Automation.ControlType]::$ct } catch {}
|
||||
if ($ctObj -ne $null) {
|
||||
$ctCond = New-Object System.Windows.Automation.PropertyCondition(
|
||||
$cond = New-Object System.Windows.Automation.PropertyCondition(
|
||||
[System.Windows.Automation.AutomationElement]::ControlTypeProperty, $ctObj)
|
||||
$cond = New-Object System.Windows.Automation.AndCondition($nameCond, $ctCond)
|
||||
} else { $cond = $nameCond }
|
||||
} else { $cond = $nameCond }
|
||||
}
|
||||
}
|
||||
$found = $scope.FindAll([System.Windows.Automation.TreeScope]::Descendants, $cond)
|
||||
$out = New-Object System.Collections.ArrayList
|
||||
$scanned = 0
|
||||
foreach ($e in $found) {
|
||||
if ($out.Count -ge $limit) { break }
|
||||
$scanned++
|
||||
if ($scanned -gt 20000) { break }
|
||||
try {
|
||||
$nm = $e.Current.Name
|
||||
if ([string]::IsNullOrEmpty($nm)) { continue }
|
||||
if ($nm.IndexOf($text, [System.StringComparison]::OrdinalIgnoreCase) -lt 0) { continue }
|
||||
$r = $e.Current.BoundingRectangle
|
||||
if ($r.Width -le 0 -or $r.Height -le 0) { continue }
|
||||
$out.Add([pscustomobject]@{
|
||||
name = $e.Current.Name
|
||||
name = $nm
|
||||
control_type = ($e.Current.ControlType.ProgrammaticName -replace 'ControlType\\\\.','')
|
||||
automation_id = $e.Current.AutomationId
|
||||
x = [int]($r.Left + $r.Width / 2)
|
||||
|
||||
Reference in new issue
Block a user