You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:27:25