feat: 新增桌面AI拾取功能,支持视觉识别控件
This commit is contained in:
1 parent
f74d1865da
commit
85dcb94056
12 files changed
+910
-53
No files matched your search
@@ -1,4 +1,12 @@
|
||||
param([string]$Mode = "element")
|
||||
param(
|
||||
[string]$Mode = "element",
|
||||
[int]$CaptureX = 0,
|
||||
[int]$CaptureY = 0,
|
||||
[int]$CaptureWidth = 1000,
|
||||
[int]$CaptureHeight = 700,
|
||||
[string]$CapturePath = "",
|
||||
[switch]$CaptureExact
|
||||
)
|
||||
$ErrorActionPreference = "Continue";
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;
|
||||
$logPath = "D:/RPA/test-output/desktop-picker.log";
|
||||
@@ -14,6 +22,80 @@ Add-Type -AssemblyName System.Windows.Forms
|
||||
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); [DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);' -Name PickW32 -Namespace RpaPick | Out-Null
|
||||
Write-PickLog "w32 ok"
|
||||
if ($Mode -eq "capture") {
|
||||
try { [RpaPick.PickDpi]::SetProcessDpiAwarenessContext([IntPtr](-4)) | Out-Null } catch {}
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
Add-Type -MemberDefinition '[DllImport("user32.dll")] public static extern int GetSystemMetrics(int nIndex);' -Name PickMetrics -Namespace RpaPickCapture | Out-Null
|
||||
$vsX = [RpaPickCapture.PickMetrics]::GetSystemMetrics(76)
|
||||
$vsY = [RpaPickCapture.PickMetrics]::GetSystemMetrics(77)
|
||||
$vsW = [RpaPickCapture.PickMetrics]::GetSystemMetrics(78)
|
||||
$vsH = [RpaPickCapture.PickMetrics]::GetSystemMetrics(79)
|
||||
$cropW = [Math]::Min($CaptureWidth, $vsW)
|
||||
$cropH = [Math]::Min($CaptureHeight, $vsH)
|
||||
if ($CaptureExact) {
|
||||
$cropX = [Math]::Max($vsX, [Math]::Min($CaptureX, ($vsX + $vsW - $cropW)))
|
||||
$cropY = [Math]::Max($vsY, [Math]::Min($CaptureY, ($vsY + $vsH - $cropH)))
|
||||
} else {
|
||||
$cropX = [Math]::Max($vsX, [Math]::Min($CaptureX - [int]($cropW / 2), ($vsX + $vsW - $cropW)))
|
||||
$cropY = [Math]::Max($vsY, [Math]::Min($CaptureY - [int]($cropH / 2), ($vsY + $vsH - $cropH)))
|
||||
}
|
||||
$markerX = $CaptureX - $cropX
|
||||
$markerY = $CaptureY - $cropY
|
||||
if ($CapturePath) { $imagePath = $CapturePath } else { $imagePath = Join-Path $env:TEMP ("ai-pick-" + [Guid]::NewGuid().ToString("N") + ".png") }
|
||||
$imageDir = Split-Path -Parent $imagePath
|
||||
if (-not (Test-Path -LiteralPath $imageDir)) { New-Item -ItemType Directory -Path $imageDir -Force | Out-Null }
|
||||
$bitmap = New-Object System.Drawing.Bitmap $cropW, $cropH
|
||||
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
||||
$graphics.CopyFromScreen($cropX, $cropY, 0, 0, $bitmap.Size)
|
||||
$pen = New-Object System.Drawing.Pen([System.Drawing.Color]::Red, 3)
|
||||
try {
|
||||
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||
if (-not $CaptureExact) {
|
||||
$graphics.DrawLine($pen, ($markerX - 20), $markerY, ($markerX + 20), $markerY)
|
||||
$graphics.DrawLine($pen, $markerX, ($markerY - 20), $markerX, ($markerY + 20))
|
||||
$graphics.DrawRectangle($pen, ($markerX - 9), ($markerY - 9), 18, 18)
|
||||
}
|
||||
} finally {
|
||||
$pen.Dispose(); $graphics.Dispose()
|
||||
}
|
||||
$bitmap.Save($imagePath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bitmap.Dispose()
|
||||
$windowTitle = ""
|
||||
$uiaName = ""
|
||||
$uiaType = ""
|
||||
try {
|
||||
$pt = New-Object System.Windows.Point($CaptureX, $CaptureY)
|
||||
$ae = [System.Windows.Automation.AutomationElement]::FromPoint($pt)
|
||||
if ($ae) {
|
||||
$ac = $ae.Current
|
||||
$uiaName = [string]$ac.Name
|
||||
$uiaType = $ac.ControlType.ProgrammaticName.Replace("ControlType.", "")
|
||||
$walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker
|
||||
$cur = $ae
|
||||
while ($cur -and -not $cur.Equals([System.Windows.Automation.AutomationElement]::RootElement)) {
|
||||
$windowTitle = [string]$cur.Current.Name
|
||||
$cur = $walker.GetParent($cur)
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
$result = @{
|
||||
aiCapture = $true
|
||||
cursorX = $CaptureX
|
||||
cursorY = $CaptureY
|
||||
cropX = $cropX
|
||||
cropY = $cropY
|
||||
cropW = $cropW
|
||||
cropH = $cropH
|
||||
markerX = $markerX
|
||||
markerY = $markerY
|
||||
window = $windowTitle
|
||||
uiaName = $uiaName
|
||||
uiaType = $uiaType
|
||||
imagePath = $imagePath
|
||||
}
|
||||
Write-Output (ConvertTo-Json $result -Compress)
|
||||
exit 0
|
||||
}
|
||||
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 } } }
|
||||
@@ -104,6 +186,7 @@ $selIdx = -1
|
||||
$prevSpace = $false
|
||||
$prevPgUp = $false
|
||||
$prevPgDn = $false
|
||||
$prevF2 = $false
|
||||
Write-PickLog "loop start"
|
||||
while ($true) {
|
||||
try {
|
||||
@@ -111,6 +194,7 @@ while ($true) {
|
||||
$esc = (([RpaPick.PickW32]::GetAsyncKeyState(27) -band 0x8001) -ne 0)
|
||||
$pgUp = (([RpaPick.PickW32]::GetAsyncKeyState(33) -band 0x8001) -ne 0)
|
||||
$pgDn = (([RpaPick.PickW32]::GetAsyncKeyState(34) -band 0x8001) -ne 0)
|
||||
$f2 = (([RpaPick.PickW32]::GetAsyncKeyState(113) -band 0x8001) -ne 0)
|
||||
if ($esc) { Write-PickLog "esc"; break }
|
||||
if ($stage -eq "hover") {
|
||||
$ptX = [System.Windows.Forms.Cursor]::Position.X
|
||||
@@ -119,9 +203,24 @@ while ($true) {
|
||||
if ($el) {
|
||||
$c = $el.Current
|
||||
$ctrlName = $c.ControlType.ProgrammaticName.Replace("ControlType.", "")
|
||||
$r = $c.BoundingRectangle; Set-HighlightBox ([int]$r.X) ([int]$r.Y) ([int]$r.Width) ([int]$r.Height)
|
||||
$infoLabel.Text = "名称: " + $c.Name + " 类型: " + $ctrlName + " [空格]锁定 [Esc]取消"
|
||||
$r = $c.BoundingRectangle
|
||||
$paneX = [int]$r.X; $paneY = [int]$r.Y; $paneW = [int]$r.Width; $paneH = [int]$r.Height
|
||||
Set-HighlightBox $paneX $paneY $paneW $paneH
|
||||
$aiHint = if ($ctrlName -eq "Pane") { " [F2]AI识别" } else { "" }
|
||||
$infoLabel.Text = "名称: " + $c.Name + " 类型: " + $ctrlName + " [空格]锁定" + $aiHint + " [Esc]取消"
|
||||
} else { Hide-Highlight }
|
||||
if ($f2 -and -not $prevF2 -and $el -and $ctrlName -eq "Pane") {
|
||||
Write-PickLog "f2 ai"
|
||||
$walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker
|
||||
$cur = $el
|
||||
$topTitle = ""
|
||||
while ($cur -and -not $cur.Equals([System.Windows.Automation.AutomationElement]::RootElement)) {
|
||||
$topTitle = [string]$cur.Current.Name
|
||||
$cur = $walker.GetParent($cur)
|
||||
}
|
||||
$result = @{ aiRequested = $true; cursorX = $ptX; cursorY = $ptY; window = $topTitle; name = [string]$c.Name; controlType = $ctrlName; paneX = $paneX; paneY = $paneY; paneW = $paneW; paneH = $paneH }
|
||||
break
|
||||
}
|
||||
if ($space -and -not $prevSpace -and $el) {
|
||||
Write-PickLog "space lock"
|
||||
$stage = "locked"
|
||||
@@ -162,6 +261,7 @@ while ($true) {
|
||||
$prevSpace = $space
|
||||
$prevPgUp = $pgUp
|
||||
$prevPgDn = $pgDn
|
||||
$prevF2 = $f2
|
||||
Start-Sleep -Milliseconds 60
|
||||
}
|
||||
$info.Close()
|
||||
|
||||
Reference in new issue
Block a user