PowerShell如何区分同进程的两个记事本窗口并分别设置位置
问题原因
核心原因:Windows 10 1903及以上版本的默认记事本采用单进程多窗口架构,多个打开的记事本窗口会共享同一个进程ID,你按照PID筛选进程后调用Set-Window时,会默认操作该进程下所有可见窗口,因此两次调用会把所有窗口都设置为最后一次指定的位置。
解决方案
方案1:修改记事本配置(最简单,可复用原有代码)
操作步骤:
- 打开任意记事本窗口,点击顶部「设置」按钮(齿轮图标)
- 在「常规」选项卡下找到「启动」分类
- 勾选「在单独的进程中打开每个窗口」
- 重启所有记事本窗口,之后每个记事本窗口都会对应独立的PID,你最初的按PID调用
Set-Window的代码即可正常运行。
方案2:不改配置,按窗口句柄区分操作
不需要修改记事本设置,直接通过Win32 API枚举同一进程下的所有独立窗口,用唯一的窗口句柄(HWnd)区分不同窗口进行定位:
首先粘贴以下代码导入所需的Win32 API:
Add-Type @" using System; using System.Runtime.InteropServices; public class Win32 { public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); } "@
然后运行以下代码获取所有记事本窗口对象:
$notepadList = Get-Process notepad $allNotepadWindows = @() foreach ($proc in $notepadList) { [Win32]::EnumWindows({ param($hWnd, $lParam) if ([Win32]::IsWindowVisible($hWnd)) { $pidOut = 0 [Win32]::GetWindowThreadProcessId($hWnd, [ref]$pidOut) if ($pidOut -eq $proc.Id) { $titleBuilder = New-Object System.Text.StringBuilder 256 [Win32]::GetWindowText($hWnd, $titleBuilder, $titleBuilder.Capacity) | Out-Null $allNotepadWindows += [PSCustomObject]@{ HWnd = $hWnd Title = $titleBuilder.ToString() ProcessId = $pidOut } } } return $true }, [IntPtr]::Zero) }
运行后输入$allNotepadWindows即可看到所有记事本窗口,每个窗口都有唯一的HWnd属性,根据标题区分你要移动的两个窗口后,直接调用MoveWindow API设置位置即可:
# 第一个窗口放左侧 [Win32]::MoveWindow($allNotepadWindows[0].HWnd, -8, 0, 976, 1038, $true) # 第二个窗口放右侧 [Win32]::MoveWindow($allNotepadWindows[1].HWnd, 952, 0, 976, 1038, $true)
原代码失效说明
你使用Start-Job的方案无效,是因为后台任务中获取到的记事本进程和前台是同一个,没有做窗口区分,调用Set-Window时仍然会操作该进程下的所有窗口,相当于覆盖了前一次的位置设置。
内容的提问来源于stack exchange,提问作者ArthurR
相关产品推荐
相关产品推荐

