如何在Start-Process后重新聚焦PowerShell脚本窗口?
问题:启动外部进程后恢复PowerShell脚本窗口焦点
我有一个带菜单的PowerShell脚本,用于日常执行各类操作。其中一项操作通过Start-Process启动进程,但启动后新进程窗口会获得焦点,我希望脚本窗口能重新获得焦点。
- 不想使用
PassThru、NoNewWindow参数(避免进程在脚本内启动) - 不想用
Minimized/Hidden窗口样式(仍需可见新进程窗口) - 尝试调用
user32.dll的SetForegroundWindow方法,脚本以管理员运行,但仅能让脚本窗口在任务栏闪烁,无法聚焦 - 脚本另一部分用VBS的
AppActivate可成功聚焦其他窗口并执行按键,但用于脚本自身窗口时无效
尝试过的方法
方法1:调用user32.dll的SetForegroundWindow
Start-Process -FilePath "MyProcessPath" Start-Sleep -Seconds 1 Add-Type @" using System; using System.Runtime.InteropServices; public class WinAp { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); } "@ $p = Get-Process | Where-Object { $_.MainWindowTitle -like "*ScriptTitle*" -and $_.ProcessName -eq "powershell" } $h = $p.MainWindowHandle [void] [WinAp]::SetForegroundWindow($h) [void] [WinAp]::ShowWindow($h, 1)
方法2:VBS的AppActivate(仅对外部窗口有效)
$tempPath = [System.IO.Path]::GetTempFileName().Replace(".tmp", ".vbs") $code = @' Set WshShell = WScript.CreateObject("WScript.Shell") WshShell.AppActivate "App.exe" WScript.Sleep 250 WshShell.SendKeys "{ENTER}" '@ $code | Out-File $tempPath -Encoding ASCII Start-Process "wscript.exe" $tempPath -Wait Remove-Item $tempPath
解决方案
方案1:修正user32.dll调用,绕过前台激活限制
Windows对SetForegroundWindow有严格的权限限制,当脚本进程不是当前前台进程的拥有者时,直接调用只会触发任务栏闪烁。可以通过AllowSetForegroundWindow授权后再调用SetForegroundWindow:
Start-Process -FilePath "MyProcessPath" Start-Sleep -Milliseconds 500 # 可根据实际启动速度调整等待时间 Add-Type @" using System; using System.Runtime.InteropServices; public class WinAp { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool AllowSetForegroundWindow(uint dwProcessId); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); } "@ # 获取当前前台窗口的进程ID,授权其允许脚本进程设置前台 $foregroundHwnd = [WinAp]::GetForegroundWindow() [WinAp]::GetWindowThreadProcessId($foregroundHwnd, [ref]$foregroundPid) | Out-Null [WinAp]::AllowSetForegroundWindow($foregroundPid) | Out-Null # 定位脚本窗口并激活 $scriptProcess = Get-Process | Where-Object { $_.MainWindowTitle -like "*ScriptTitle*" -and $_.ProcessName -eq "powershell" } if ($scriptProcess) { [WinAp]::AllowSetForegroundWindow($scriptProcess.Id) | Out-Null [void][WinAp]::SetForegroundWindow($scriptProcess.MainWindowHandle) }
方案2:用VBS的AppActivate结合脚本进程ID
之前VBS失效是因为窗口标题匹配不准确,直接使用脚本的进程ID($PID)可以精准定位:
Start-Process -FilePath "MyProcessPath" Start-Sleep -Milliseconds 500 # 获取当前脚本的进程ID $scriptPid = $PID $tempPath = [System.IO.Path]::GetTempFileName().Replace(".tmp", ".vbs") $code = @" Set WshShell = WScript.CreateObject("WScript.Shell") WScript.Sleep 200 WshShell.AppActivate $scriptPid "@ $code | Out-File $tempPath -Encoding ASCII Start-Process "wscript.exe" $tempPath -Wait -NoNewWindow Remove-Item $tempPath
内容的提问来源于stack exchange,提问作者Lusiiky
相关产品推荐
相关产品推荐

