param([string]$Mode = "element") $ErrorActionPreference = "Continue" 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 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 } } } 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) $info.Size = New-Object System.Drawing.Size(560, 64) $info.BackColor = [System.Drawing.Color]::FromArgb(15, 40, 90) $info.Opacity = 0.92 $infoLabel = New-Object System.Windows.Forms.Label $infoLabel.Dock = [System.Windows.Forms.DockStyle]::Fill $infoLabel.ForeColor = [System.Drawing.Color]::White $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 $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 Get-DeepestAtPoint([double]$px, [double]$py) { $point = New-Object System.Windows.Automation.Point($px, $py) $cur = [System.Windows.Automation.AutomationElement]::FromPoint($point) if (-not $cur) { return $null } for ($depth = 0; $depth -lt 25; $depth++) { $children = $cur.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition) $next = $null $nextArea = [double]::MaxValue foreach ($c in $children) { try { $r = $c.Current.BoundingRectangle if ($r.Width -gt 1 -and $r.Height -gt 1 -and $px -ge $r.X -and $px -le ($r.X + $r.Width) -and $py -ge $r.Y -and $py -le ($r.Y + $r.Height)) { $area = $r.Width * $r.Height if ($area -lt $nextArea) { $next = $c; $nextArea = $area } } } catch {} } if (-not $next) { break } $cur = $next } return $cur } $info.Show() $hl.Show() $stage = "hover" $chain = New-Object System.Collections.ArrayList $selIdx = -1 $prevSpace = $false $prevPgUp = $false $prevPgDn = $false 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 $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) } $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) { $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 } } $prevSpace = $space $prevPgUp = $pgUp $prevPgDn = $pgDn Start-Sleep -Milliseconds 60 } $info.Close() $hl.Close() if ($result) { Write-Output (ConvertTo-Json $result -Compress) } else { Write-Output (ConvertTo-Json @{ cancelled = $true } -Compress) }