tools: add user-silent offline rpa window mode

This commit is contained in:
zhaoyilun
2026-07-11 15:11:03 +08:00
parent d1a1e499ea
commit e8b4110cd2
2 changed files with 202 additions and 14 deletions

View File

@@ -9,10 +9,14 @@ param(
[int]$Y = 120,
[int]$Width = 270,
[int]$Height = 570,
[ValidateSet("Visible", "Offscreen", "Minimized")]
[string]$WindowMode = "Visible",
[switch]$SkipExtract,
[switch]$NoLaunch,
[switch]$KeepExisting,
[switch]$TopMost,
[switch]$NoActivate,
[switch]$UserSilent,
[switch]$NoScreenshot
)
@@ -20,6 +24,20 @@ $ErrorActionPreference = "Stop"
$repo = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")
$InfoPromptTitle = -join ([char[]](0x4FE1, 0x606F, 0x63D0, 0x793A))
if ($UserSilent) {
$WindowMode = "Offscreen"
$NoActivate = $true
$NoScreenshot = $true
$TopMost = $false
}
$effectiveX = $X
$effectiveY = $Y
if ($WindowMode -eq "Offscreen") {
$effectiveX = -32000
$effectiveY = -32000
}
function Resolve-RepoPath([string]$PathValue) {
if ([System.IO.Path]::IsPathRooted($PathValue)) {
return $PathValue
@@ -89,8 +107,11 @@ public static class ISphereOfflineWindowApi {
"@ -ErrorAction SilentlyContinue
}
function Move-ClientWindows([object[]]$Windows, [int]$MainX, [int]$MainY, [int]$MainWidth, [int]$MainHeight, [bool]$MakeTopMost) {
function Move-ClientWindows([object[]]$Windows, [int]$MainX, [int]$MainY, [int]$MainWidth, [int]$MainHeight, [bool]$MakeTopMost, [bool]$Activate, [string]$Mode) {
$hwndTop = [IntPtr]::Zero
$topMostHandle = [IntPtr]::new(-1)
$swpShowWindow = 0x0040
$swpNoActivate = 0x0010
$mainWindow = $null
$promptWindow = $null
foreach ($window in $Windows) {
@@ -101,18 +122,32 @@ function Move-ClientWindows([object[]]$Windows, [int]$MainX, [int]$MainY, [int]$
$hwnd = [IntPtr]::new($hwndValue)
[ISphereOfflineWindowApi]::ShowWindow($hwnd, 9) | Out-Null
if ([string]$window.title -eq $InfoPromptTitle) {
[ISphereOfflineWindowApi]::MoveWindow($hwnd, ($MainX + $MainWidth + 40), ($MainY + 40), 300, 190, $true) | Out-Null
[ISphereOfflineWindowApi]::SetForegroundWindow($hwnd) | Out-Null
$promptX = if ($Mode -eq "Offscreen") { $MainX } else { $MainX + $MainWidth + 40 }
$promptY = if ($Mode -eq "Offscreen") { $MainY + $MainHeight + 40 } else { $MainY + 40 }
$flags = $swpShowWindow
if (-not $Activate) { $flags = $flags -bor $swpNoActivate }
[ISphereOfflineWindowApi]::SetWindowPos($hwnd, $hwndTop, $promptX, $promptY, 300, 190, [uint32]$flags) | Out-Null
if ($Activate) {
[ISphereOfflineWindowApi]::SetForegroundWindow($hwnd) | Out-Null
}
if ($Mode -eq "Minimized") {
[ISphereOfflineWindowApi]::ShowWindow($hwnd, 6) | Out-Null
}
$promptWindow = $window
}
else {
if ($MakeTopMost) {
[ISphereOfflineWindowApi]::SetWindowPos($hwnd, $topMostHandle, $MainX, $MainY, $MainWidth, $MainHeight, 0x0040) | Out-Null
$insertAfter = if ($MakeTopMost) { $topMostHandle } else { $hwndTop }
$flags = $swpShowWindow
if (-not $Activate) {
$flags = $flags -bor $swpNoActivate
}
else {
[ISphereOfflineWindowApi]::MoveWindow($hwnd, $MainX, $MainY, $MainWidth, $MainHeight, $true) | Out-Null
[ISphereOfflineWindowApi]::SetWindowPos($hwnd, $insertAfter, $MainX, $MainY, $MainWidth, $MainHeight, [uint32]$flags) | Out-Null
if ($Activate) {
[ISphereOfflineWindowApi]::SetForegroundWindow($hwnd) | Out-Null
}
if ($Mode -eq "Minimized") {
[ISphereOfflineWindowApi]::ShowWindow($hwnd, 6) | Out-Null
}
[ISphereOfflineWindowApi]::SetForegroundWindow($hwnd) | Out-Null
$mainWindow = $window
}
}
@@ -176,7 +211,12 @@ if (-not $KeepExisting) {
$process = $null
if (-not $NoLaunch) {
$process = Start-Process -FilePath $clientExe -WorkingDirectory $clientDir -PassThru
if ($UserSilent -or $WindowMode -ne "Visible" -or $NoActivate) {
$process = Start-Process -FilePath $clientExe -WorkingDirectory $clientDir -WindowStyle Minimized -PassThru
}
else {
$process = Start-Process -FilePath $clientExe -WorkingDirectory $clientDir -PassThru
}
}
$deadline = (Get-Date).AddSeconds([Math]::Max(1, $WaitSeconds))
@@ -197,7 +237,8 @@ if ($windows.Count -eq 0) {
throw "no visible IMPlatformClient window found. Last scan: $($scan | ConvertTo-Json -Depth 12 -Compress)"
}
$moveResult = Move-ClientWindows -Windows $windows -MainX $X -MainY $Y -MainWidth $Width -MainHeight $Height -MakeTopMost ([bool]$TopMost)
$activateWindows = -not [bool]$NoActivate
$moveResult = Move-ClientWindows -Windows $windows -MainX $effectiveX -MainY $effectiveY -MainWidth $Width -MainHeight $Height -MakeTopMost ([bool]$TopMost) -Activate $activateWindows -Mode $WindowMode
Start-Sleep -Seconds 1
$scanAfterMove = Invoke-HelperJson -Op "scan_windows" -OpArgs @{ include_all_visible = $false } -RequestId "offline-real-client-scan-after-move"
@@ -215,8 +256,17 @@ if ($latePromptWindows.Count -gt 0) {
$promptHwndValue = [Convert]::ToInt64(($latePromptWindow.hwnd -replace "^0x", ""), 16)
$promptHwnd = [IntPtr]::new($promptHwndValue)
[ISphereOfflineWindowApi]::ShowWindow($promptHwnd, 9) | Out-Null
[ISphereOfflineWindowApi]::MoveWindow($promptHwnd, ($X + $Width + 40), ($Y + 40), 300, 190, $true) | Out-Null
[ISphereOfflineWindowApi]::SetForegroundWindow($promptHwnd) | Out-Null
$latePromptX = if ($WindowMode -eq "Offscreen") { $effectiveX } else { $effectiveX + $Width + 40 }
$latePromptY = if ($WindowMode -eq "Offscreen") { $effectiveY + $Height + 40 } else { $effectiveY + 40 }
$latePromptFlags = 0x0040
if (-not $activateWindows) { $latePromptFlags = $latePromptFlags -bor 0x0010 }
[ISphereOfflineWindowApi]::SetWindowPos($promptHwnd, [IntPtr]::Zero, $latePromptX, $latePromptY, 300, 190, [uint32]$latePromptFlags) | Out-Null
if ($activateWindows) {
[ISphereOfflineWindowApi]::SetForegroundWindow($promptHwnd) | Out-Null
}
if ($WindowMode -eq "Minimized") {
[ISphereOfflineWindowApi]::ShowWindow($promptHwnd, 6) | Out-Null
}
}
Start-Sleep -Milliseconds 500
$scanAfterMove = Invoke-HelperJson -Op "scan_windows" -OpArgs @{ include_all_visible = $false } -RequestId "offline-real-client-scan-after-prompt-move"
@@ -245,14 +295,23 @@ if ($mainWindow) {
}
$screenshotFile = $null
if (-not $NoScreenshot) {
if (-not $NoScreenshot -and $WindowMode -eq "Visible") {
$screenshotFile = Join-Path $runOut ("open-offline-real-client-{0}.png" -f ((Get-Date).ToString("yyyyMMdd-HHmmss")))
$captureWidth = if ($promptWindow) { $Width + 420 } else { $Width + 80 }
Save-WindowScreenshot -OutputPath $screenshotFile -ScreenX $X -ScreenY $Y -CaptureWidth $captureWidth -CaptureHeight ($Height + 80)
Save-WindowScreenshot -OutputPath $screenshotFile -ScreenX $effectiveX -ScreenY $effectiveY -CaptureWidth $captureWidth -CaptureHeight ($Height + 80)
}
[pscustomobject]@{
ok = $true
user_silent = [bool]$UserSilent
window_mode = $WindowMode
no_activate = [bool]$NoActivate
effective_bounds = @{
x = $effectiveX
y = $effectiveY
width = $Width
height = $Height
}
archive = $archiveFull
extracted_dir = $extractFull
client_root = $ClientRoot