初始提交:WinUI 3 一键运行轮盘工具
This commit is contained in:
+329
@@ -0,0 +1,329 @@
|
||||
# End-to-end test: summon modes (click / long-press / hold), execute-by-click, swipe select, mouse position, global switch.
|
||||
param(
|
||||
[string]$Exe = "src/OneClickRun.App/bin/x64/Debug/net8.0-windows10.0.19041.0/win-x64/OneClickRun.exe"
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$exePath = Join-Path $root $Exe
|
||||
if (-not (Test-Path $exePath)) { throw "App not found: $exePath" }
|
||||
|
||||
$settingsDir = Join-Path $env:APPDATA "OneClickRun"
|
||||
$settingsFile = Join-Path $settingsDir "settings.json"
|
||||
$settingsBackup = $null
|
||||
if (Test-Path $settingsFile) {
|
||||
$settingsBackup = $settingsFile + ".e2e-backup"
|
||||
Copy-Item $settingsFile $settingsBackup -Force
|
||||
}
|
||||
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
public class E2EWin {
|
||||
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lParam);
|
||||
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder sb, int max);
|
||||
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
||||
[DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
|
||||
[DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y);
|
||||
[DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
|
||||
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
|
||||
}
|
||||
"@
|
||||
|
||||
$script:Proc = $null
|
||||
$script:TargetPid = 0
|
||||
$script:WheelHwnd = [IntPtr]::Zero
|
||||
$script:WheelRect = New-Object E2EWin+RECT
|
||||
$script:WheelVisible = $false
|
||||
|
||||
function Get-WheelInfo {
|
||||
$script:WheelVisible = $false
|
||||
$script:WheelHwnd = [IntPtr]::Zero
|
||||
$cb = [E2EWin+EnumWindowsProc]{
|
||||
param($h, $l)
|
||||
$wpid = 0
|
||||
[void][E2EWin]::GetWindowThreadProcessId($h, [ref]$wpid)
|
||||
if ($wpid -eq $script:TargetPid) {
|
||||
$sb = New-Object System.Text.StringBuilder 256
|
||||
[void][E2EWin]::GetWindowText($h, $sb, 256)
|
||||
if ($sb.ToString() -like '*OneClickRun Wheel*') {
|
||||
$script:WheelHwnd = $h
|
||||
$rect = New-Object E2EWin+RECT
|
||||
[void][E2EWin]::GetWindowRect($h, [ref]$rect)
|
||||
$script:WheelRect = $rect
|
||||
$script:WheelVisible = [E2EWin]::IsWindowVisible($h)
|
||||
}
|
||||
}
|
||||
return $true
|
||||
}
|
||||
[void][E2EWin]::EnumWindows($cb, [IntPtr]::Zero)
|
||||
return $script:WheelVisible
|
||||
}
|
||||
|
||||
function Write-Settings([hashtable]$s) {
|
||||
$s | ConvertTo-Json -Depth 10 | Set-Content -Path $settingsFile -Encoding UTF8
|
||||
}
|
||||
|
||||
function Start-App {
|
||||
$script:Proc = Start-Process -FilePath $exePath -PassThru
|
||||
$script:TargetPid = $script:Proc.Id
|
||||
Start-Sleep -Seconds 4
|
||||
if ($script:Proc.HasExited) { throw "App exited early with code $($script:Proc.ExitCode)" }
|
||||
}
|
||||
|
||||
function Stop-App {
|
||||
if ($script:Proc -and -not $script:Proc.HasExited) { Stop-Process -Id $script:TargetPid -Force }
|
||||
Start-Sleep -Milliseconds 400
|
||||
}
|
||||
|
||||
function Key-Down([byte]$vk) { [E2EWin]::keybd_event($vk, 0, 0, [UIntPtr]::Zero) }
|
||||
function Key-Up([byte]$vk) { [E2EWin]::keybd_event($vk, 0, 2, [UIntPtr]::Zero) }
|
||||
|
||||
function Press-Hotkey { Key-Down 0x11; Key-Down 0x12; Key-Down 0x51; Start-Sleep -Milliseconds 120; Key-Up 0x51; Key-Up 0x12; Key-Up 0x11 }
|
||||
function Hold-Hotkey-Down { Key-Down 0x11; Key-Down 0x12; Key-Down 0x51 }
|
||||
function Hold-Hotkey-Up { Key-Up 0x51; Key-Up 0x12; Key-Up 0x11 }
|
||||
function Press-Esc { Key-Down 0x1B; Start-Sleep -Milliseconds 60; Key-Up 0x1B }
|
||||
|
||||
function Assert([bool]$cond, [string]$msg) { if (-not $cond) { throw "Assertion failed: $msg" } }
|
||||
|
||||
function Get-SectorPoint([int]$index, [int]$count) {
|
||||
$w = $script:WheelRect.Right - $script:WheelRect.Left
|
||||
$scale = $w / 560.0
|
||||
$cx = ($script:WheelRect.Left + $script:WheelRect.Right) / 2.0
|
||||
$cy = ($script:WheelRect.Top + $script:WheelRect.Bottom) / 2.0
|
||||
$span = 360.0 / $count
|
||||
$mid = ($index + 0.5) * $span
|
||||
$rad = $mid * [Math]::PI / 180.0
|
||||
$r = 177.5 * $scale
|
||||
return @{ X = [int][Math]::Round($cx + $r * [Math]::Sin($rad)); Y = [int][Math]::Round($cy - $r * [Math]::Cos($rad)) }
|
||||
}
|
||||
|
||||
function Click-At([int]$x, [int]$y) {
|
||||
[void][E2EWin]::SetCursorPos($x, $y)
|
||||
Start-Sleep -Milliseconds 120
|
||||
[E2EWin]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 80
|
||||
[E2EWin]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
|
||||
function Click-Once([int]$x, [int]$y) {
|
||||
[void][E2EWin]::SetCursorPos($x, $y)
|
||||
Start-Sleep -Milliseconds 60
|
||||
[E2EWin]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 40
|
||||
[E2EWin]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 30
|
||||
}
|
||||
|
||||
function New-NotepadCommand { @{ Name = "Notepad"; ActionType = 0; Target = (Join-Path $env:WINDIR 'System32\notepad.exe'); IconGlyph = "A"; ColorIndex = 0 } }
|
||||
function New-CalcCommand { @{ Name = "Calc"; ActionType = 0; Target = (Join-Path $env:WINDIR 'System32\calc.exe'); IconGlyph = "B"; ColorIndex = 1 } }
|
||||
function New-BingCommand { @{ Name = "Bing"; ActionType = 2; Target = "https://www.bing.com"; IconGlyph = "C"; ColorIndex = 2 } }
|
||||
|
||||
function New-BaseSettings([int]$summon, [int]$selection, [int]$position, [bool]$enabled, [bool]$startWithWindows = $false) {
|
||||
return @{
|
||||
SchemaVersion = 1; StartWithWindows = $startWithWindows; WheelEnabled = $enabled
|
||||
WheelPosition = $position; Hotkey = @{ Modifiers = 3; Key = 81 }
|
||||
SummonMode = $summon; LongPressDelayMs = 400; SelectionMode = $selection; ThemeMode = 2
|
||||
Commands = @((New-NotepadCommand), (New-CalcCommand), (New-BingCommand))
|
||||
}
|
||||
}
|
||||
|
||||
$failed = 0
|
||||
$results = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
function Run-Test([string]$name, [scriptblock]$body) {
|
||||
try {
|
||||
& $body
|
||||
$results.Add("PASS $name")
|
||||
Write-Host "PASS $name"
|
||||
} catch {
|
||||
$script:failed++
|
||||
$results.Add("FAIL $name :: $($_.Exception.Message)")
|
||||
Write-Host "FAIL $name :: $($_.Exception.Message)" -ForegroundColor Red
|
||||
} finally {
|
||||
Stop-App
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Run-Test "click summon: show then toggle-hide" {
|
||||
Remove-Item $settingsFile -ErrorAction SilentlyContinue
|
||||
Start-App
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not show wheel"
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (-not (Get-WheelInfo)) "second hotkey press did not hide wheel"
|
||||
}
|
||||
|
||||
Run-Test "click summon: Esc dismisses wheel" {
|
||||
Remove-Item $settingsFile -ErrorAction SilentlyContinue
|
||||
Start-App
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not show wheel"
|
||||
Press-Esc; Start-Sleep -Milliseconds 500
|
||||
Assert (-not (Get-WheelInfo)) "wheel still visible after Esc"
|
||||
}
|
||||
|
||||
Run-Test "click execute: sector 0 starts Notepad" {
|
||||
Remove-Item $settingsFile -ErrorAction SilentlyContinue
|
||||
Start-App
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not show wheel"
|
||||
$p = Get-SectorPoint 0 5
|
||||
Click-At $p.X $p.Y
|
||||
Start-Sleep -Milliseconds 800
|
||||
Assert (-not (Get-WheelInfo)) "wheel not hidden after execute"
|
||||
$np = Get-Process notepad -ErrorAction SilentlyContinue
|
||||
Assert ($null -ne $np) "Notepad process was not started"
|
||||
$np | Stop-Process -Force
|
||||
}
|
||||
|
||||
Run-Test "long-press summon: shows after 400ms and stays after release" {
|
||||
Write-Settings (New-BaseSettings 1 0 0 $true)
|
||||
Start-App
|
||||
Hold-Hotkey-Down
|
||||
Start-Sleep -Milliseconds 300
|
||||
Assert (-not (Get-WheelInfo)) "wheel shown too early (300ms)"
|
||||
Start-Sleep -Milliseconds 250
|
||||
Assert (Get-WheelInfo) "wheel not shown after 400ms long press"
|
||||
Hold-Hotkey-Up
|
||||
Start-Sleep -Milliseconds 400
|
||||
Assert (Get-WheelInfo) "wheel disappeared after release (should stay)"
|
||||
Press-Esc; Start-Sleep -Milliseconds 400
|
||||
Assert (-not (Get-WheelInfo)) "Esc did not close wheel"
|
||||
}
|
||||
|
||||
Run-Test "hold summon: visible while held, hidden on release" {
|
||||
Write-Settings (New-BaseSettings 2 0 0 $true)
|
||||
Start-App
|
||||
Hold-Hotkey-Down
|
||||
Start-Sleep -Milliseconds 400
|
||||
Assert (Get-WheelInfo) "wheel not visible while hotkey held"
|
||||
Hold-Hotkey-Up
|
||||
Start-Sleep -Milliseconds 400
|
||||
Assert (-not (Get-WheelInfo)) "wheel still visible after release"
|
||||
}
|
||||
|
||||
Run-Test "swipe select: hold + mouse up, release executes Notepad" {
|
||||
Write-Settings (New-BaseSettings 2 2 0 $true)
|
||||
Start-App
|
||||
Hold-Hotkey-Down
|
||||
Start-Sleep -Milliseconds 400
|
||||
Assert (Get-WheelInfo) "wheel not visible while hotkey held"
|
||||
$p = Get-SectorPoint 0 3
|
||||
[void][E2EWin]::SetCursorPos($p.X, $p.Y)
|
||||
Start-Sleep -Milliseconds 150
|
||||
Hold-Hotkey-Up
|
||||
Start-Sleep -Milliseconds 800
|
||||
Assert (-not (Get-WheelInfo)) "wheel still visible after release"
|
||||
$np = Get-Process notepad -ErrorAction SilentlyContinue
|
||||
Assert ($null -ne $np) "swipe did not execute Notepad"
|
||||
$np | Stop-Process -Force
|
||||
}
|
||||
|
||||
Run-Test "wheel position: mouse cursor" {
|
||||
Write-Settings (New-BaseSettings 0 0 1 $true)
|
||||
Start-App
|
||||
[void][E2EWin]::SetCursorPos(400, 300)
|
||||
Start-Sleep -Milliseconds 150
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not show wheel"
|
||||
$cx = ($script:WheelRect.Left + $script:WheelRect.Right) / 2.0
|
||||
$cy = ($script:WheelRect.Top + $script:WheelRect.Bottom) / 2.0
|
||||
$d = [Math]::Sqrt(($cx - 400) * ($cx - 400) + ($cy - 300) * ($cy - 300))
|
||||
Assert ($d -lt 12) "wheel center offset from cursor by $([int]$d)px"
|
||||
Press-Esc
|
||||
}
|
||||
|
||||
Run-Test "double-click selection: first click pending, second click executes" {
|
||||
Write-Settings (New-BaseSettings 0 1 0 $true)
|
||||
Start-App
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not show wheel"
|
||||
$p = Get-SectorPoint 0 3
|
||||
Click-Once $p.X $p.Y
|
||||
Start-Sleep -Milliseconds 150
|
||||
Assert (Get-WheelInfo) "wheel hidden after first click in double-click mode"
|
||||
$np = Get-Process notepad -ErrorAction SilentlyContinue
|
||||
Assert ($null -eq $np) "Notepad started after single click in double-click mode"
|
||||
Click-Once $p.X $p.Y
|
||||
Start-Sleep -Milliseconds 600
|
||||
Assert (-not (Get-WheelInfo)) "wheel not hidden after double click"
|
||||
$np = Get-Process notepad -ErrorAction SilentlyContinue
|
||||
Assert ($null -ne $np) "Notepad not started after double click"
|
||||
$np | Stop-Process -Force
|
||||
}
|
||||
|
||||
Run-Test "autostart: registry Run value written at launch" {
|
||||
Write-Settings (New-BaseSettings 0 0 0 $true $true)
|
||||
Start-App
|
||||
$runKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'
|
||||
$prev = (Get-ItemProperty -Path $runKey -Name OneClickRun -ErrorAction SilentlyContinue).OneClickRun
|
||||
try {
|
||||
$cur = (Get-ItemProperty -Path $runKey -Name OneClickRun -ErrorAction SilentlyContinue).OneClickRun
|
||||
Assert ($null -ne $cur) "autostart registry value missing"
|
||||
Assert ($cur -like '*OneClickRun*') "unexpected autostart value: $cur"
|
||||
} finally {
|
||||
if ($null -eq $prev) {
|
||||
Remove-ItemProperty -Path $runKey -Name OneClickRun -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
Set-ItemProperty -Path $runKey -Name OneClickRun -Value $prev
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Run-Test "script execute: cmd and ps1 sectors run" {
|
||||
$cmdPath = Join-Path $env:TEMP "ocr-e2e-run.cmd"
|
||||
$ps1Path = Join-Path $env:TEMP "ocr-e2e-run.ps1"
|
||||
$cmdMarker = Join-Path $env:TEMP "ocr-e2e-cmd-marker.txt"
|
||||
$ps1Marker = Join-Path $env:TEMP "ocr-e2e-ps1-marker.txt"
|
||||
Remove-Item $cmdMarker, $ps1Marker -ErrorAction SilentlyContinue
|
||||
$cmdLine = 'echo ok > "' + $cmdMarker + '"'
|
||||
$ps1Line = "Set-Content -Path '" + $ps1Marker + "' -Value ok"
|
||||
Set-Content -Path $cmdPath -Value @('@echo off', $cmdLine) -Encoding ASCII
|
||||
Set-Content -Path $ps1Path -Value $ps1Line -Encoding ASCII
|
||||
|
||||
$settings = New-BaseSettings 0 0 0 $true
|
||||
$settings.Commands = @(
|
||||
@{ Name = "RunCmd"; ActionType = 3; Target = $cmdPath; IconGlyph = "D"; ColorIndex = 0 },
|
||||
@{ Name = "RunPs1"; ActionType = 3; Target = $ps1Path; IconGlyph = "E"; ColorIndex = 1 }
|
||||
)
|
||||
Write-Settings $settings
|
||||
Start-App
|
||||
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not show wheel"
|
||||
$p0 = Get-SectorPoint 0 2
|
||||
Click-At $p0.X $p0.Y
|
||||
Start-Sleep -Milliseconds 1200
|
||||
Assert (Test-Path $cmdMarker) "cmd script did not run"
|
||||
Assert (-not (Get-WheelInfo)) "wheel not hidden after cmd execute"
|
||||
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 500
|
||||
Assert (Get-WheelInfo) "hotkey did not re-show wheel"
|
||||
$p1 = Get-SectorPoint 1 2
|
||||
Click-At $p1.X $p1.Y
|
||||
Start-Sleep -Milliseconds 1200
|
||||
Assert (Test-Path $ps1Marker) "ps1 script did not run"
|
||||
|
||||
Remove-Item $cmdMarker, $ps1Marker, $cmdPath, $ps1Path -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Run-Test "global switch off: wheel cannot be summoned" {
|
||||
Write-Settings (New-BaseSettings 0 0 0 $false)
|
||||
Start-App
|
||||
Press-Hotkey; Start-Sleep -Milliseconds 600
|
||||
Assert (-not (Get-WheelInfo)) "wheel summoned while global switch off"
|
||||
}
|
||||
} finally {
|
||||
Stop-App
|
||||
if ($settingsBackup) { Move-Item $settingsBackup $settingsFile -Force } else { Remove-Item $settingsFile -ErrorAction SilentlyContinue }
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=== E2E RESULT: {0}/{1} passed ===" -f ($results.Count - $failed), $results.Count)
|
||||
exit $failed
|
||||
@@ -0,0 +1,19 @@
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$bmp = New-Object System.Drawing.Bitmap(256, 256)
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||
$colors = @('#E5484D', '#F76B15', '#F5A623', '#30A46C', '#12A594', '#0091FF', '#5E5CE6', '#BF5AF2')
|
||||
$rect = New-Object System.Drawing.Rectangle(8, 8, 240, 240)
|
||||
for ($i = 0; $i -lt 8; $i++) {
|
||||
$brush = New-Object System.Drawing.SolidBrush([System.Drawing.ColorTranslator]::FromHtml($colors[$i]))
|
||||
$g.FillPie($brush, $rect, ($i * 45 - 90), 46)
|
||||
$brush.Dispose()
|
||||
}
|
||||
$white = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White)
|
||||
$g.FillEllipse($white, 78, 78, 100, 100)
|
||||
$white.Dispose()
|
||||
$g.Dispose()
|
||||
$out = Join-Path (Split-Path -Parent $PSScriptRoot) 'src/OneClickRun.App/Assets/AppIcon.png'
|
||||
$bmp.Save($out, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bmp.Dispose()
|
||||
Write-Host "icon saved to $out"
|
||||
@@ -0,0 +1,17 @@
|
||||
Add-Type @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class HKTest {
|
||||
[DllImport("user32.dll", SetLastError=true)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint mods, uint vk);
|
||||
[DllImport("user32.dll")] public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
[DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow();
|
||||
}
|
||||
'@
|
||||
$h = [HKTest]::GetConsoleWindow()
|
||||
Write-Host "hwnd=$h"
|
||||
foreach ($spec in @(@('Ctrl+Alt+Q', 3, 0x51), @('Ctrl+Alt+K', 3, 0x4B), @('Ctrl+Alt+W', 3, 0x57), @('Ctrl+Shift+Space', 6, 0x20))) {
|
||||
$ok = [HKTest]::RegisterHotKey($h, 1, [uint32]$spec[1], [uint32]$spec[2])
|
||||
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
Write-Host ($spec[0] + ' => ' + $ok + ' err=' + $err)
|
||||
if ($ok) { [void][HKTest]::UnregisterHotKey($h, 1) }
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# 冒烟测试:启动应用 → 验证主窗口 → Ctrl+Alt+Q 呼出轮盘 → Esc 关闭轮盘 → 退出
|
||||
param(
|
||||
[string]$Exe = "src/OneClickRun.App/bin/x64/Debug/net8.0-windows10.0.19041.0/win-x64/OneClickRun.exe"
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$exePath = Join-Path $root $Exe
|
||||
if (-not (Test-Path $exePath)) { throw "找不到应用: $exePath" }
|
||||
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
public class WinEnum {
|
||||
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lParam);
|
||||
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder sb, int max);
|
||||
[DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
|
||||
}
|
||||
"@
|
||||
|
||||
$script:Wins = New-Object System.Collections.Generic.List[object]
|
||||
$script:TargetPid = 0
|
||||
$cb = [WinEnum+EnumWindowsProc]{
|
||||
param($h, $l)
|
||||
$wpid = 0
|
||||
[void][WinEnum]::GetWindowThreadProcessId($h, [ref]$wpid)
|
||||
if ($wpid -eq $script:TargetPid) {
|
||||
$sb = New-Object System.Text.StringBuilder 256
|
||||
[void][WinEnum]::GetWindowText($h, $sb, 256)
|
||||
$script:Wins.Add([pscustomobject]@{ Visible = [WinEnum]::IsWindowVisible($h); Title = $sb.ToString() })
|
||||
}
|
||||
return $true
|
||||
}
|
||||
function Show-Windows {
|
||||
$script:Wins.Clear()
|
||||
[void][WinEnum]::EnumWindows($cb, [IntPtr]::Zero)
|
||||
$script:Wins | ForEach-Object { Write-Host (" visible={0} title='{1}'" -f $_.Visible, $_.Title) }
|
||||
}
|
||||
|
||||
$proc = Start-Process -FilePath $exePath -PassThru
|
||||
$script:TargetPid = $proc.Id
|
||||
try {
|
||||
Write-Host "已启动 PID=$($proc.Id)"
|
||||
Start-Sleep -Seconds 5
|
||||
if ($proc.HasExited) { throw "进程提前退出,退出码 $($proc.ExitCode)" }
|
||||
Write-Host "=== 启动后窗口 ==="
|
||||
Show-Windows
|
||||
if (-not ($script:Wins | Where-Object { $_.Title -like '*一键运行*' })) { throw "未找到主窗口" }
|
||||
|
||||
# 发送 Ctrl+Alt+Q
|
||||
[WinEnum]::keybd_event(0x11, 0, 0, [UIntPtr]::Zero)
|
||||
[WinEnum]::keybd_event(0x12, 0, 0, [UIntPtr]::Zero)
|
||||
[WinEnum]::keybd_event(0x51, 0, 0, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 120
|
||||
[WinEnum]::keybd_event(0x51, 0, 2, [UIntPtr]::Zero)
|
||||
[WinEnum]::keybd_event(0x12, 0, 2, [UIntPtr]::Zero)
|
||||
[WinEnum]::keybd_event(0x11, 0, 2, [UIntPtr]::Zero)
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "=== 按 Ctrl+Alt+Q 后 ==="
|
||||
Show-Windows
|
||||
$wheel = $script:Wins | Where-Object { $_.Title -like '*Wheel*' }
|
||||
if (-not $wheel) { throw "热键未能呼出轮盘窗口" }
|
||||
if (-not $wheel.Visible) { throw "轮盘窗口存在但不可见" }
|
||||
Write-Host "PASS: 轮盘已呼出"
|
||||
|
||||
# Esc 关闭
|
||||
[WinEnum]::keybd_event(0x1B, 0, 0, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 80
|
||||
[WinEnum]::keybd_event(0x1B, 0, 2, [UIntPtr]::Zero)
|
||||
Start-Sleep -Milliseconds 800
|
||||
Write-Host "=== 按 Esc 后 ==="
|
||||
Show-Windows
|
||||
$wheel2 = $script:Wins | Where-Object { $_.Title -like '*Wheel*' }
|
||||
if ($wheel2 -and $wheel2.Visible) { throw "Esc 后轮盘仍可见" }
|
||||
Write-Host "PASS: 轮盘已关闭"
|
||||
|
||||
Write-Host "SMOKE OK"
|
||||
}
|
||||
finally {
|
||||
if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force }
|
||||
}
|
||||
Reference in New Issue
Block a user