如何通过PowerShell切换聚焦已运行的指定UWP应用
PowerShell实现UWP窗口前台聚焦方案
问题核心原因
常规Win32应用的窗口句柄直接归属自身进程,因此通过进程对象的MainWindowHandle属性可以直接拿到可用句柄。但UWP应用运行在AppContainer沙盒中,其可见顶层窗口由系统ApplicationFrameHost进程托管,UWP自身进程对象的MainWindowHandle属性固定返回0,直接对0值句柄调用前台切换API必然失效。
实现逻辑
- 枚举系统所有顶层可见窗口,筛选类名为
ApplicationFrameWindow、归属对应FrameHost宿主、和目标UWP关联的真实窗口句柄 - 对最小化状态的窗口先发送恢复指令,再调用
SetForegroundWindow切换到前台 - 若未找到已运行的对应窗口,直接调用系统原生
IApplicationActivationManager接口通过UWP的AppUserModelID(AUMID)激活应用,避免权限拦截
可直接运行的完整脚本
Add-Type @" using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; public class Win32 { public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IApplicationActivationManager { int ActivateApplication([In] string appUserModelId, [In] string arguments, [In] uint activateOptions, out uint processId); } [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] public class ApplicationActivationManager : IApplicationActivationManager { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)][DispId(1)] public extern int ActivateApplication([In] string appUserModelId, [In] string arguments, [In] uint activateOptions, out uint processId); } } "@ # -------------------------- 配置项 按需修改 -------------------------- $targetProcessName = "Microsoft.WindowsStore" # 目标UWP的进程名 $targetAumid = "Microsoft.WindowsStore_8wekyb3d8bbwe!App" # 目标UWP的AUMID $SW_RESTORE = 9 # 窗口恢复指令值 # ------------------------------------------------------------------- # 检查目标UWP是否已运行 $runningUwp = Get-Process -Name $targetProcessName -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $runningUwp) { # 未运行则直接通过AUMID激活 $activationMgr = New-Object Win32.ApplicationActivationManager $targetPid = 0 $activationMgr.ActivateApplication($targetAumid, $null, 0, [ref]$targetPid) | Out-Null exit } # 枚举所有顶层窗口匹配目标UWP的可见宿主窗口 $foundHwnd = [IntPtr]::Zero $frameHostProcess = Get-Process ApplicationFrameHost -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -eq $runningUwp.MainWindowTitle } | Select-Object -First 1 if ($frameHostProcess) { $enumCallback = [Win32+EnumWindowsProc]{ param($hWnd, $lParam) if (-not [Win32]::IsWindowVisible($hWnd)) { return $true } $windowPid = 0 [Win32]::GetWindowThreadProcessId($hWnd, [ref]$windowPid) | Out-Null if ($windowPid -ne $frameHostProcess.Id) { return $true } $classBuffer = New-Object System.Text.StringBuilder 256 [Win32]::GetClassName($hWnd, $classBuffer, 256) | Out-Null if ($classBuffer.ToString() -eq "ApplicationFrameWindow") { $script:foundHwnd = $hWnd return $false } return $true } [Win32]::EnumWindows($enumCallback, [IntPtr]::Zero) | Out-Null } # 执行前台切换 if ($foundHwnd -ne [IntPtr]::Zero) { [Win32]::ShowWindow($foundHwnd, $SW_RESTORE) | Out-Null [Win32]::SetForegroundWindow($foundHwnd) | Out-Null } else { # 未找到有效窗口则走激活流程 $activationMgr = New-Object Win32.ApplicationActivationManager $targetPid = 0 $activationMgr.ActivateApplication($targetAumid, $null, 0, [ref]$targetPid) | Out-Null }
使用说明
- 替换脚本开头配置项里的进程名和AUMID为目标UWP的对应值即可。执行
Get-StartApps可查询当前系统所有已安装UWP的名称和对应AUMID,进程名可以在任务管理器的对应应用详情页查到。 - 脚本默认匹配和UWP窗口标题一致的FrameHost窗口,如果需要切换UWP多开的某个指定窗口,可以额外增加窗口标题的精确匹配规则。
- 系统对前台窗口切换有UAC权限限制,脚本运行时如果有全屏独占程序处于顶层,
SetForegroundWindow调用会被系统拦截,属于正常机制限制。
内容的提问来源于stack exchange,提问作者Rel
相关产品推荐
相关产品推荐

