Windows平台通过应用ID启动应用并获取进程ID(PID)的实现方案咨询
Windows平台通过应用ID启动应用并获取进程ID(PID)的实现方案咨询
我完全理解你现在遇到的困境:不管是用PowerShell的start命令通过shell:AppsFolder\<AppID>启动应用(无论UWP还是普通Win32程序),还是用Rust调用ShellExecuteW API,都没法直接获取到启动后进程的PID;而IApplicationActivationManager虽然能返回PID,但只支持UWP应用,没法覆盖所有场景。下面给你分PowerShell和Rust两种场景,提供针对性的解决思路:
一、PowerShell 实现方案
1. 分应用类型处理(推荐)
先通过应用ID判断是UWP还是普通Win32应用,再分别处理:
- 普通Win32应用:从
Get-StartApps里拿到应用的实际可执行文件路径,用Start-Process的-PassThru参数直接获取包含PID的进程对象 - UWP应用:调用
IApplicationActivationManagerCOM接口启动并获取PID
示例代码:
# 1. 获取目标应用信息(替换为你的目标AppID) $targetApp = Get-StartApps | Where-Object { $_.AppID -eq "308046B0AF4A39CB" } # 2. 判断应用类型并启动获取PID if ($targetApp.AppID -match '^Microsoft\.|^[0-9A-F]{16,}$') { # 识别为UWP应用,使用IApplicationActivationManager $activationManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]::Parse("{45BA127D-10A8-46EA-8AB7-56EA9078943C}"))) $pid = $activationManager.ActivateApplication($targetApp.AppID, $null, 0, [ref]0) Write-Host "UWP应用启动成功,PID: $pid" } else { # 识别为普通Win32应用,尝试获取可执行文件路径 $appPath = $null # 先从注册表卸载项查找 $uninstallEntry = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" | Where-Object { $_.DisplayName -eq $targetApp.Name } if ($uninstallEntry) { $appPath = Join-Path $uninstallEntry.InstallLocation "$($targetApp.Name).exe" } # 注册表找不到就从开始菜单快捷方式解析 if (-not $appPath) { $shortcutPath = Get-ChildItem -Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs" -Recurse -Filter "*.lnk" | Where-Object { $_.Name -like "*$($targetApp.Name)*" } | Select-Object -First 1 -ExpandProperty FullName $shell = New-Object -ComObject WScript.Shell $appPath = $shell.CreateShortcut($shortcutPath).TargetPath } # 启动并获取PID $process = Start-Process -FilePath $appPath -PassThru Write-Host "Win32应用启动成功,PID: $($process.Id)" }
2. 通用 fallback 方案(通过进程名/窗口查找)
如果类型判断失效,还可以在启动应用后,通过进程名或者窗口标题匹配查找PID(注意:如果存在多个同名进程,结果可能不准确):
# 启动目标应用 start 'shell:AppsFolder\<你的AppID>' # 等待1秒确保进程启动完成 Start-Sleep -Seconds 1 # 通过进程名查找(替换为目标应用的进程名,比如Firefox) $pid = (Get-Process -Name "firefox" | Select-Object -Last 1).Id Write-Host "找到PID: $pid"
二、Rust 实现方案
1. 分场景调用不同API
和PowerShell思路一致,分普通Win32应用和UWP应用分别处理:
- 普通Win32应用:放弃
ShellExecuteW,改用CreateProcessWAPI,它能直接返回包含PID的PROCESS_INFORMATION结构体 - UWP应用:继续使用
IApplicationActivationManagerCOM接口,获取启动后的PID
处理普通Win32应用的Rust代码示例
use std::ptr; use windows::core::{PCWSTR, Result}; use windows::Win32::Foundation::{BOOL, STARTF_USESHOWWINDOW}; use windows::Win32::System::Threading::{CreateProcessW, PROCESS_INFORMATION, STARTUPINFOW}; use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; fn launch_win32_app(app_path: &str) -> Result<u32> { let app_path_wide: Vec<u16> = app_path.encode_utf16().chain(Some(0)).collect(); let mut startup_info = STARTUPINFOW::default(); startup_info.cb = std::mem::size_of::<STARTUPINFOW>() as u32; startup_info.dwFlags = STARTF_USESHOWWINDOW.0; startup_info.wShowWindow = SW_SHOWNORMAL.0; let mut process_info = PROCESS_INFORMATION::default(); let success = unsafe { CreateProcessW( PCWSTR::null(), PCWSTR::from_raw(app_path_wide.as_ptr()), ptr::null_mut(), ptr::null_mut(), BOOL(0), 0, ptr::null_mut(), PCWSTR::null(), &startup_info, &mut process_info, ) }; if success.as_bool() { // 关闭不需要的句柄,避免资源泄漏 unsafe { windows::Win32::System::Threading::CloseHandle(process_info.hProcess); windows::Win32::System::Threading::CloseHandle(process_info.hThread); } Ok(process_info.dwProcessId) } else { Err(windows::core::Error::from_win32()) } }
处理UWP应用的Rust代码示例(基于IApplicationActivationManager)
use std::ptr; use windows::core::{ComInterface, PCWSTR, Result}; use windows::Win32::System::Com::{CoInitializeEx, COINIT_APARTMENTTHREADED}; #[windows::interface("45BA127D-10A8-46EA-8AB7-56EA9078943C")] #[allow(non_snake_case)] trait IApplicationActivationManager: ComInterface { fn ActivateApplication( &self, appUserModelId: &PCWSTR, arguments: &PCWSTR, options: u32, processId: *mut u32, ) -> windows::core::HRESULT; } fn launch_uwp_app(app_id: &str) -> Result<u32> { // 初始化COM环境 unsafe { CoInitializeEx(ptr::null(), COINIT_APARTMENTTHREADED)?; } // 创建IApplicationActivationManager实例 let activation_manager: IApplicationActivationManager = windows::core::factory::<IApplicationActivationManager>()?.create_instance()?; let app_id_wide: Vec<u16> = app_id.encode_utf16().chain(Some(0)).collect(); let mut pid = 0; // 启动应用并获取PID unsafe { activation_manager.ActivateApplication( PCWSTR::from_raw(app_id_wide.as_ptr()), PCWSTR::null(), 0, &mut pid, )?; } Ok(pid) }
2. 通用 fallback:启动后通过窗口查找PID
如果无法提前区分应用类型,也可以在启动后通过窗口标题匹配获取PID,Rust里可以用EnumWindows API遍历窗口,再通过GetWindowThreadProcessId获取对应PID:
use std::ptr; use windows::core::Result; use windows::Win32::Foundation::{HWND, LRESULT, WPARAM}; use windows::Win32::UI::WindowsAndMessaging::{EnumWindows, GetWindowTextW, GetWindowThreadProcessId}; fn find_pid_by_window_title(title_substring: &str) -> Option<u32> { let title_wide: Vec<u16> = title_substring.encode_utf16().chain(Some(0)).collect(); let mut found_pid = None; unsafe { EnumWindows( Some(enum_windows_callback), WPARAM( (&mut found_pid, title_wide.as_ptr()) as *mut (Option<u32>, *const u16) as usize, ), ); } found_pid } unsafe extern "system" fn enum_windows_callback(hwnd: HWND, lparam: WPARAM) -> i32 { let (found_pid, target_title_ptr) = *(lparam.0 as *mut (Option<u32>, *const u16)); if found_pid.is_some() { return 0; // 已找到目标,停止枚举 } // 获取当前窗口标题 let mut window_title = [0u16; 256]; let title_len = GetWindowTextW(hwnd, &mut window_title); if title_len == 0 { return 1; // 无标题窗口,跳过 } // 转换为字符串并匹配 let target_title = String::from_utf16_lossy(std::slice::from_raw_parts(target_title_ptr, 256)); let window_title_str = String::from_utf16_lossy(&window_title[..title_len as usize]); if window_title_str.contains(&target_title) { let mut pid = 0; GetWindowThreadProcessId(hwnd, Some(&mut pid)); *found_pid.as_mut().unwrap() = Some(pid); return 0; } 1 // 继续枚举下一个窗口 }
关键注意事项
shell:AppsFolder\<AppID>是Shell层的高层启动方式,会由Shell进程代为处理,因此无法直接获取子进程的PID,必须换用能直接控制进程创建的API(如CreateProcessW)或UWP专用的COM接口- UWP应用的AppID通常带有
Microsoft.前缀或为一串长GUID,普通Win32应用的AppID一般与可执行文件名或路径相关,可通过此特征提前区分应用类型 - 启动后建议等待1-2秒再执行PID查找操作,避免进程尚未完全启动导致查找失败
内容来源于stack exchange




