fix: 拾取脚本完整重写——v3 逻辑+全链路日志(此前骨架覆盖事故修复)
This commit is contained in:
parent
a1490303d4
commit
ed9b9daa50
@ -1,28 +1,29 @@
|
||||
param([string]$Mode = "element")
|
||||
$ErrorActionPreference = "Continue"
|
||||
$ErrorActionPreference = "Continue";
|
||||
$logPath = "D:/RPA/test-output/desktop-picker.log";
|
||||
function Write-PickLog([string]$msg) {
|
||||
try { Add-Content -Path $logPath -Value ((Get-Date -Format "HH:mm:ss.fff") + " | " + $msg) -Encoding UTF8 } catch {}
|
||||
}
|
||||
Write-PickLog ("=== start Mode=$Mode ===")
|
||||
Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes, WindowsBase
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type -MemberDefinition "[DllImport(`"user32.dll`")] public static extern short GetAsyncKeyState(int key); [DllImport(`"user32.dll`", CharSet = CharSet.Unicode)] public static extern int GetWindowTextW(IntPtr h, System.Text.StringBuilder sb, int max);" -Name PickW32 -Namespace RpaPick | Out-Null
|
||||
|
||||
Write-PickLog "assemblies ok"
|
||||
Add-Type -MemberDefinition "[DllImport(\"user32.dll\")] public static extern short GetAsyncKeyState(int key); [DllImport(\"user32.dll\", CharSet = CharSet.Unicode)] public static extern int GetWindowTextW(IntPtr h, System.Text.StringBuilder sb, int max);" -Name PickW32 -Namespace RpaPick | Out-Null
|
||||
Write-PickLog "w32 ok"
|
||||
if ($Mode -eq "windows") {
|
||||
$wins = @()
|
||||
foreach ($p in Get-Process) {
|
||||
if ($p.MainWindowHandle -ne [IntPtr]::Zero -and $p.MainWindowTitle) {
|
||||
$wins += @{ title = $p.MainWindowTitle; process = $p.ProcessName }
|
||||
}
|
||||
}
|
||||
foreach ($p in Get-Process) { if ($p.MainWindowHandle -ne [IntPtr]::Zero -and $p.MainWindowTitle) { $wins += @{ title = $p.MainWindowTitle; process = $p.ProcessName } } }
|
||||
Write-PickLog "windows: $($_wins.Count)"
|
||||
Write-Output (ConvertTo-Json $wins -Compress -AsArray)
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ===== 信息条(屏幕顶部居中,显眼) =====
|
||||
$info = New-Object System.Windows.Forms.Form
|
||||
$info.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
|
||||
$info.TopMost = $true
|
||||
$info.ShowInTaskbar = $false
|
||||
$info.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
|
||||
$sw = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width
|
||||
$info.Location = New-Object System.Drawing.Point(([int]($sw / 2) - 280), 10)
|
||||
$sw2 = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width
|
||||
$info.Location = New-Object System.Drawing.Point(([int]($sw2 / 2) - 280), 10)
|
||||
$info.Size = New-Object System.Drawing.Size(560, 64)
|
||||
$info.BackColor = [System.Drawing.Color]::FromArgb(15, 40, 90)
|
||||
$info.Opacity = 0.92
|
||||
@ -33,8 +34,6 @@ $infoLabel.Font = New-Object System.Drawing.Font("Microsoft YaHei", 11)
|
||||
$infoLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
|
||||
$infoLabel.Text = "移动鼠标到目标元素上"
|
||||
$info.Controls.Add($infoLabel)
|
||||
|
||||
# ===== 高亮框(半透明红块跟随) =====
|
||||
$hl = New-Object System.Windows.Forms.Form
|
||||
$hl.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
|
||||
$hl.TopMost = $true
|
||||
@ -42,14 +41,12 @@ $hl.ShowInTaskbar = $false
|
||||
$hl.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
|
||||
$hl.BackColor = [System.Drawing.Color]::Red
|
||||
$hl.Opacity = 0.35
|
||||
|
||||
function Set-HighlightBox([int]$x, [int]$y, [int]$w, [int]$h) {
|
||||
$hl.Location = New-Object System.Drawing.Point(($x - 2), ($y - 2))
|
||||
$hl.Size = New-Object System.Drawing.Size(($w + 4), ($h + 4))
|
||||
$hl.Show()
|
||||
}
|
||||
|
||||
# ===== 下钻到包含点的最小后代 =====
|
||||
function Hide-Highlight { $hl.Hide() }
|
||||
function Get-DeepestAtPoint([double]$px, [double]$py) {
|
||||
$point = New-Object System.Windows.Automation.Point($px, $py)
|
||||
$cur = [System.Windows.Automation.AutomationElement]::FromPoint($point)
|
||||
@ -72,89 +69,69 @@ function Get-DeepestAtPoint([double]$px, [double]$py) {
|
||||
}
|
||||
return $cur
|
||||
}
|
||||
|
||||
$info.Show()
|
||||
$hl.Show()
|
||||
|
||||
$stage = "hover"
|
||||
$chain = New-Object System.Collections.ArrayList
|
||||
$selIdx = -1
|
||||
$script:lastX = -1
|
||||
$script:lastY = -1
|
||||
$script:lastMove = Get-Date
|
||||
$script:pending = $false
|
||||
$prevSpace = $false
|
||||
$prevPgUp = $false
|
||||
$prevPgDn = $false
|
||||
|
||||
Write-PickLog "loop start"
|
||||
while ($true) {
|
||||
$space = ([RpaPick.PickW32]::GetAsyncKeyState(32) -band 0x8000) -ne 0
|
||||
$esc = ([RpaPick.PickW32]::GetAsyncKeyState(27) -band 0x8000) -ne 0
|
||||
$pgUp = ([RpaPick.PickW32]::GetAsyncKeyState(33) -band 0x8000) -ne 0
|
||||
$pgDn = ([RpaPick.PickW32]::GetAsyncKeyState(34) -band 0x8000) -ne 0
|
||||
|
||||
if ($esc) { break }
|
||||
|
||||
if ($stage -eq "hover") {
|
||||
$ptX = [System.Windows.Forms.Cursor]::Position.X
|
||||
$ptY = [System.Windows.Forms.Cursor]::Position.Y
|
||||
if ($ptX -ne $script:lastX -or $ptY -ne $script:lastY) {
|
||||
$script:lastX = $ptX; $script:lastY = $ptY
|
||||
$script:lastMove = Get-Date
|
||||
$script:pending = $true
|
||||
}
|
||||
$idle = ((Get-Date) - $script:lastMove).TotalMilliseconds -ge 250
|
||||
if (-not $idle) { Start-Sleep -Milliseconds 60; return }
|
||||
$el = Get-DeepestAtPoint $ptX $ptY
|
||||
if ($el) {
|
||||
$c = $el.Current
|
||||
$ctrlName = $c.ControlType.ProgrammaticName.Replace("ControlType.", "")
|
||||
Set-HighlightBox $ptX $ptY 1 1
|
||||
$r = $c.Current.BoundingRectangle
|
||||
if ($r.Width -gt 0) { Set-HighlightBox ([int]$r.X) ([int]$r.Y) ([int]$r.Width) ([int]$r.Height) }
|
||||
$infoLabel.Text = "名称: " + $c.Name + " 类型: " + $ctrlName + " [空格]锁定 [Esc]取消"
|
||||
}
|
||||
if ($space -and -not $prevSpace -and $el) {
|
||||
$stage = "locked"
|
||||
$chain = New-Object System.Collections.ArrayList
|
||||
$walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker
|
||||
$cur = $el
|
||||
while ($cur -and -not $cur.Equals([System.Windows.Automation.AutomationElement]::RootElement)) {
|
||||
$c = $cur.Current
|
||||
$r = $c.BoundingRectangle
|
||||
[void]$chain.Insert(0, @{ el = $cur; name = [string]$c.Name; automationId = [string]$c.AutomationId; controlType = $c.ControlType.ProgrammaticName.Replace("ControlType.", ""); x = [int]$r.X; y = [int]$r.Y; w = [int]$r.Width; h = [int]$r.Height })
|
||||
$cur = $walker.GetParent($cur)
|
||||
try {
|
||||
$space = ([RpaPick.PickW32]::GetAsyncKeyState(32) -band 0x8000) -ne 0
|
||||
$esc = ([RpaPick.PickW32]::GetAsyncKeyState(27) -band 0x8000) -ne 0
|
||||
$pgUp = ([RpaPick.PickW32]::GetAsyncKeyState(33) -band 0x8000) -ne 0
|
||||
$pgDn = ([RpaPick.PickW32]::GetAsyncKeyState(34) -band 0x8000) -ne 0
|
||||
if ($esc) { Write-PickLog "esc"; break }
|
||||
if ($stage -eq "hover") {
|
||||
$ptX = [System.Windows.Forms.Cursor]::Position.X
|
||||
$ptY = [System.Windows.Forms.Cursor]::Position.Y
|
||||
$el = Get-DeepestAtPoint $ptX $ptY
|
||||
if ($el) {
|
||||
$c = $el.Current
|
||||
$ctrlName = $c.ControlType.ProgrammaticName.Replace("ControlType.", "")
|
||||
Set-HighlightBox ([int]$c.Current.BoundingRectangle.X) ([int]$c.Current.BoundingRectangle.Y) ([int]$c.Current.BoundingRectangle.Width) ([int]$c.Current.BoundingRectangle.Height)
|
||||
$infoLabel.Text = "名称: " + $c.Name + " 类型: " + $ctrlName + " [空格]锁定 [Esc]取消"
|
||||
} else { Hide-Highlight }
|
||||
if ($space -and -not $prevSpace -and $el) {
|
||||
Write-PickLog "space lock"
|
||||
$stage = "locked"
|
||||
$chain = New-Object System.Collections.ArrayList
|
||||
$walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker
|
||||
$cur = $el
|
||||
while ($cur -and -not $cur.Equals([System.Windows.Automation.AutomationElement]::RootElement)) {
|
||||
$c = $cur.Current
|
||||
$r = $c.BoundingRectangle
|
||||
[void]$chain.Insert(0, @{ el = $cur; name = [string]$c.Name; automationId = [string]$c.AutomationId; controlType = $c.ControlType.ProgrammaticName.Replace("ControlType.", ""); x = [int]$r.X; y = [int]$r.Y; w = [int]$r.Width; h = [int]$r.Height })
|
||||
$cur = $walker.GetParent($cur)
|
||||
}
|
||||
$selIdx = $chain.Count - 1
|
||||
Write-PickLog ("locked chain=" + $chain.Count)
|
||||
$infoLabel.Text = "已锁定 [" + ($selIdx + 1) + "/" + $chain.Count + "] [PageUp/PageDown]调层级 [空格]确认回填 [Esc]取消"
|
||||
}
|
||||
$selIdx = $chain.Count - 1
|
||||
$infoLabel.Text = "已锁定 [" + ($selIdx + 1) + "/" + $chain.Count + "] [PageUp/PageDown]调层级 [空格]确认回填 [Esc]取消"
|
||||
}
|
||||
}
|
||||
|
||||
if ($stage -eq "locked") {
|
||||
$item = $chain[$selIdx]
|
||||
if ($item) {
|
||||
Set-HighlightBox $item.x $item.y $item.w $item.h
|
||||
$infoLabel.Text = "层级 " + ($selIdx + 1) + "/" + $chain.Count + " 名称: " + $item.name + " 类型: " + $item.controlType + " [PageUp/PageDown]调层级 [空格]确认 [Esc]取消"
|
||||
}
|
||||
if ($pgUp -and -not $prevPgUp -and $selIdx -gt 0) { $selIdx -= 1 }
|
||||
if ($pgDn -and -not $prevPgDn -and $selIdx -lt ($chain.Count - 1)) { $selIdx += 1 }
|
||||
if ($space -and -not $prevSpace) {
|
||||
if ($stage -eq "locked") {
|
||||
$item = $chain[$selIdx]
|
||||
$rootTitle = ""
|
||||
foreach ($it in $chain) { if ($it.name) { $rootTitle = $it.name; break } }
|
||||
$result = @{ selected = $selIdx; window = $rootTitle; name = $item.name; automationId = $item.automationId; controlType = $item.controlType; x = $item.x; y = $item.y; w = $item.w; h = $item.h }
|
||||
break
|
||||
if ($item) {
|
||||
Set-HighlightBox $item.x $item.y $item.w $item.h
|
||||
$infoLabel.Text = "层级 " + ($selIdx + 1) + "/" + $chain.Count + " 名称: " + $item.name + " 类型: " + $item.controlType + " [PageUp/PageDown]调层级 [空格]确认 [Esc]取消"
|
||||
}
|
||||
if ($pgUp -and -not $prevPgUp -and $selIdx -gt 0) { $selIdx -= 1 }
|
||||
if ($pgDn -and -not $prevPgDn -and $selIdx -lt ($chain.Count - 1)) { $selIdx += 1 }
|
||||
if ($space -and -not $prevSpace) {
|
||||
$item = $chain[$selIdx]
|
||||
$rootTitle = ""
|
||||
foreach ($it in $chain) { if ($it.name) { $rootTitle = $it.name; break } }
|
||||
$result = @{ selected = $selIdx; window = $rootTitle; name = $item.name; automationId = $item.automationId; controlType = $item.controlType; x = $item.x; y = $item.y; w = $item.w; h = $item.h }
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-PickLog ("LOOP ERR: " + $_.Exception.Message)
|
||||
}
|
||||
|
||||
$prevSpace = $space
|
||||
$prevPgUp = $pgUp
|
||||
$prevPgDn = $pgDn
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
Start-Sleep -Milliseconds 60
|
||||
}
|
||||
|
||||
$info.Close()
|
||||
$hl.Close()
|
||||
|
||||
if ($result) { Write-Output (ConvertTo-Json $result -Compress) } else { Write-Output (ConvertTo-Json @{ cancelled = $true } -Compress) }
|
||||
Loading…
Reference in New Issue
Block a user