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 } # ---- 信息窗 ---- $form = New-Object System.Windows.Forms.Form $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None $form.TopMost = $true $form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual $form.Location = New-Object System.Drawing.Point(20, 20) $form.Size = New-Object System.Drawing.Size(480, 110) $form.BackColor = [System.Drawing.Color]::FromArgb(20, 60, 120) $label = New-Object System.Windows.Forms.Label $label.Dock = [System.Windows.Forms.DockStyle]::Fill $label.ForeColor = [System.Drawing.Color]::White $label.Font = New-Object System.Drawing.Font("Microsoft YaHei", 10) $form.Controls.Add($label) # ---- 高亮窗(半透明红块) ---- $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 # ---- 状态 ---- $stage = "hover" $chain = New-Object System.Collections.ArrayList $selIdx = -1 $prevPageUp = $false $prevPageDown = $false $prevSpace = $false function Show-Element($el) { if ($null -eq $el) { $script:hl.Hide(); return } $r = $el.Current.BoundingRectangle if ($r.Width -le 0 -or $r.Height -le 0) { $script:hl.Hide(); return } $script:hl.Location = New-Object System.Drawing.Point([int]($r.X - 2), [int]($r.Y - 2)) $script:hl.Size = New-Object System.Drawing.Size([int]($r.Width + 4), [int]($r.Height + 4)) $script:hl.Show() } $form.Show() $hl.Show() 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 $point = New-Object System.Windows.Automation.Point($ptX, $ptY) $el = $null try { $el = [System.Windows.Automation.AutomationElement]::FromPoint($point) } catch {} if ($el) { $c = $el.Current $ctrlName = $c.ControlType.ProgrammaticName.Replace("ControlType.", "") $label.Text = "名称: " + $c.Name + [Environment]::NewLine + "类型: " + $ctrlName + [Environment]::NewLine + "空格=锁定 Esc=取消" Show-Element $el } if ($space -and -not $prevSpace -and $el) { # 进入微调:收集从根到命中的元素链 $walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker $chain.Clear() $cur = $el while ($cur -and -not $cur.Equals([System.Windows.Automation.AutomationElement]::RootElement)) { $cc = $cur.Current $rr = $cc.BoundingRectangle [void]$chain.Insert(0, @{ el = $cur; name = [string]$cc.Name; automationId = [string]$cc.AutomationId; controlType = $cc.ControlType.ProgrammaticName.Replace("ControlType.", ""); x = [int]$rr.X; y = [int]$rr.Y; w = [int]$rr.Width; h = [int]$rr.Height }) $cur = $walker.GetParent($cur) } $selIdx = $chain.Count - 1 $stage = "adjust" $label.Text = "微调模式:PageUp/PageDown 切换层级,空格确认,Esc 取消" } } if ($stage -eq "adjust") { if ($pgUp -and -not $prevPageUp -and $selIdx -gt 0) { $selIdx -= 1 } if ($pgDn -and -not $prevPageDown -and $selIdx -lt ($chain.Count - 1)) { $selIdx += 1 } $item = $chain[$selIdx] if ($item) { $label.Text = "层级 " + ($selIdx + 1) + "/" + $chain.Count + " 名称: " + $item.name + [Environment]::NewLine + "类型: " + $item.controlType + [Environment]::NewLine + "PageUp/PageDown=切层级 空格=确认 Esc=取消" $hl.Location = New-Object System.Drawing.Point($item.x - 2, $item.y - 2) $hl.Size = New-Object System.Drawing.Size($item.w + 4, $item.h + 4) $hl.Show() } if ($space -and -not $prevSpace) { $item = $chain[$selIdx] # 窗口名 = 链中第一个带窗口句柄的层 $rootName = "" foreach ($it in $chain) { if ($it.name) { $rootName = $it.name; break } } $result = @{ selected = $selIdx; window = $rootName; name = $item.name; automationId = $item.automationId; controlType = $item.controlType; x = $item.x; y = $item.y; w = $item.w; h = $item.h } break } } $prevPageUp = $pgUp $prevPageDown = $pgDn $prevSpace = $space Start-Sleep -Milliseconds 60 } $form.Close() $hl.Close() if ($result) { Write-Output (ConvertTo-Json $result -Compress) } else { Write-Output (ConvertTo-Json @{ cancelled = $true } -Compress) }