PowerShell中用&启动第三方程序后,如何并行调用SetWindowText修改窗口标题?
PowerShell实现并行修改第三方应用窗口标题的方案
针对你的需求,PowerShell里可以用后台作业或Runspace实现类似VB/C#中BackgroundWorker的并行执行效果,以下是具体实现方案:
一、后台作业(Start-Job):快速实现并行监控
后台作业会创建独立的PowerShell进程运行标题修改逻辑,和主脚本并行执行,不会被& 'application.exe'的阻塞影响。
完整代码示例
# 导入Win32 API用于修改窗口标题 Add-Type @" using System; using System.Runtime.InteropServices; public class Win32 { [DllImport("user32.dll")] public static extern bool SetWindowText(IntPtr hWnd, string lpString); [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); } "@ # 启动后台作业:持续检测目标窗口并修改标题 $titleJob = Start-Job -ScriptBlock { param($originalTitle, $newTitle) do { # 根据应用启动后的默认标题查找窗口句柄 $windowHandle = [Win32]::FindWindow($null, $originalTitle) if ($windowHandle -ne [IntPtr]::Zero) { # 修改窗口标题 [Win32]::SetWindowText($windowHandle, $newTitle) # 如果不需要持续监控(防止应用再次改标题),可以取消下面的注释退出循环 # break } Start-Sleep -Milliseconds 100 } while ($true) } -ArgumentList "应用默认标题", "自定义新标题" # 启动第三方应用(主脚本会阻塞直到应用退出) & 'application.exe' # 应用退出后,停止并清理后台作业 Stop-Job $titleJob Remove-Job $titleJob
关键说明
- 替换
"应用默认标题"为第三方应用启动后的原始窗口标题; - 若应用会反复自动修改标题,保留
while ($true)循环持续监控;若只需修改一次,取消break的注释即可。
二、Runspace:轻量级线程级并行
Runspace是PowerShell的线程执行环境,比后台作业更高效(无需创建新进程),适合对资源占用有要求的场景。
完整代码示例
# 导入Win32 API Add-Type @" using System; using System.Runtime.InteropServices; public class Win32 { [DllImport("user32.dll")] public static extern bool SetWindowText(IntPtr hWnd, string lpString); [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); } "@ # 创建并启动Runspace $runspace = [runspacefactory]::CreateRunspace() $runspace.Open() # 定义并行执行的标题修改逻辑 $scriptBlock = { param($originalTitle, $newTitle) do { $windowHandle = [Win32]::FindWindow($null, $originalTitle) if ($windowHandle -ne [IntPtr]::Zero) { [Win32]::SetWindowText($windowHandle, $newTitle) # 可选:单次修改则取消注释break # break } Start-Sleep -Milliseconds 100 } while ($true) } # 配置Pipeline并传入参数 $pipeline = $runspace.CreatePipeline($scriptBlock) $pipeline.Input.Write([System.Management.Automation.Runspaces.Parameter]::Create("originalTitle", "应用默认标题")) $pipeline.Input.Write([System.Management.Automation.Runspaces.Parameter]::Create("newTitle", "自定义新标题")) $pipeline.Input.Close() # 异步启动Runspace逻辑 $asyncResult = $pipeline.InvokeAsync() # 启动第三方应用(主脚本阻塞直到应用退出) & 'application.exe' # 应用退出后清理Runspace资源 $pipeline.Stop() $runspace.Close() $runspace.Dispose()
补充优化:根据进程ID查找窗口
如果无法确定应用的原始标题,可以添加GetWindowThreadProcessIdAPI,通过进程ID定位窗口:
# 在Win32类中添加以下API声明 [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); # 修改后台作业/Runspace的脚本块,通过进程ID查找窗口 $targetProcess = Get-Process -Name "application" # 替换为应用进程名 do { $windowHandle = [IntPtr]::Zero # 遍历所有顶级窗口,匹配进程ID $callback = { param([IntPtr]$hwnd, [IntPtr]$lParam) $pid = 0 [Win32]::GetWindowThreadProcessId($hwnd, [ref]$pid) if ($pid -eq $lParam) { [System.Threading.Interlocked]::Exchange([ref]$windowHandle, $hwnd) | Out-Null return $false } return $true } [Win32]::EnumWindows([System.Delegate]::CreateDelegate([type]"Win32+EnumWindowsProc", $callback), $targetProcess.Id) if ($windowHandle -ne [IntPtr]::Zero) { [Win32]::SetWindowText($windowHandle, $newTitle) # break } Start-Sleep -Milliseconds 100 } while ($true)
内容的提问来源于stack exchange,提问作者Eric van Loon
相关产品推荐
相关产品推荐

