如何通过窗口句柄/进程ID(PID)在Windows 11中移动或调整应用窗口(AP_Ws)大小
如何通过窗口句柄/进程ID(PID)在Windows 11中移动或调整应用窗口(AP_Ws)大小
太懂你这种窗口标题重复的烦恼了——用标题定位窗口简直是碰运气,搞不好就改错了目标窗口。用PID或者句柄确实是精准操作的正道,之前AutoIt的WinMove导致窗口冻住,大概率是没拿到正确的顶层主窗口句柄(比如误操作了进程里的子窗口/后台隐藏窗口),或者脚本权限没给够。
下面针对你熟悉的几种语言,给你直接能用的实操方案,都是基于Windows原生API的操作,不会出现窗口冻住的问题:
一、Python方案(最推荐,易上手)
Python的pywin32库直接封装了Windows系统API,步骤清晰,调试也方便:先通过PID找到对应的主窗口句柄,再调用SetWindowPos完成移动/ resize操作。
步骤1:安装依赖
先在命令行装pywin32:
pip install pywin32
步骤2:代码示例
import win32gui import win32process import win32con def get_main_hwnd_by_pid(target_pid): # 枚举所有顶层窗口,筛选出对应PID的可见启用主窗口 def enum_callback(hwnd, extra_data): # 只处理可见、能交互的窗口 if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd): _, pid = win32process.GetWindowThreadProcessId(hwnd) if pid == target_pid: extra_data.append(hwnd) return True matched_hwnds = [] win32gui.EnumWindows(enum_callback, matched_hwnds) # 返回第一个匹配的主窗口(如果有多个可自己加判断逻辑) return matched_hwnds[0] if matched_hwnds else None def move_resize_window(hwnd, x, y, width, height): # 调用SetWindowPos调整窗口,SWP_SHOWWINDOW确保窗口正常显示 win32gui.SetWindowPos( hwnd, win32con.HWND_TOP, # 把窗口放到最上层 x, y, width, height, win32con.SWP_SHOWWINDOW ) # 示例:把PID为1234的窗口移到屏幕(100,100)位置,大小设为800x600 if __name__ == "__main__": target_pid = 1234 # 替换成你要操作的进程PID main_hwnd = get_main_hwnd_by_pid(target_pid) if main_hwnd: move_resize_window(main_hwnd, 100, 100, 800, 600) else: print("没找到对应PID的可见主窗口,请检查PID是否正确")
小提示:get_main_hwnd_by_pid函数专门过滤了不可见、不可交互的窗口,这是避免窗口冻住的核心——别去碰进程里的后台子窗口。
二、VBScript方案
VBScript不用装额外依赖,直接调用系统API就能实现,适合不想装Python环境的场景:
代码示例
Option Explicit ' 声明要调用的Windows API Private Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As Long Private Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long Dim targetPID, targetHwnd targetPID = 1234 ' 替换成你的目标进程PID targetHwnd = 0 ' 枚举所有窗口,找到对应PID的可见主窗口 EnumWindows AddressOf EnumWindowsProc, targetPID If targetHwnd <> 0 Then ' 移动到(100,100)位置,大小设为800x600,最后一个1表示立即重绘窗口 MoveWindow targetHwnd, 100, 100, 800, 600, 1 WScript.Echo "窗口调整完成" Else WScript.Echo "未找到对应PID的可见窗口,请检查PID是否正确" End If Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Long Dim currentPID GetWindowThreadProcessId hwnd, currentPID ' 匹配PID且窗口可见时,记录句柄并停止枚举 If currentPID = lParam And IsWindowVisible(hwnd) Then targetHwnd = hwnd EnumWindowsProc = 0 Else EnumWindowsProc = 1 ' 继续枚举其他窗口 End If End Function
使用说明:保存为.vbs文件后,右键选择「以管理员身份运行」(如果操作的是管理员权限启动的程序,脚本也需要对应权限)。
三、Perl方案
Perl可以用Win32::API和Win32::GuiTest模块实现,思路和上面一致:
步骤1:安装依赖
先通过cpan安装所需模块:
cpan Win32::API Win32::GuiTest
步骤2:代码示例
use strict; use warnings; use Win32::GuiTest qw(FindWindowLike GetWindowThreadProcessId IsWindowVisible); use Win32::API; # 声明MoveWindow系统API my $MoveWindow = Win32::API->new( 'user32', 'MoveWindow', [qw(N N N N N N)], 'N' ); my $target_pid = 1234; # 替换成目标进程PID # 筛选出对应PID的可见主窗口 my @matched_windows = FindWindowLike(0, undef, undef, undef, sub { my ($hwnd) = @_; my ($tid, $pid) = GetWindowThreadProcessId($hwnd); return $pid == $target_pid && IsWindowVisible($hwnd); }); if (@matched_windows) { my $main_hwnd = $matched_windows[0]; # 移动到(100,100),大小设为800x600,1表示立即重绘 $MoveWindow->Call($main_hwnd, 100, 100, 800, 600, 1); print "窗口调整完成\n"; } else { print "未找到对应PID的可见窗口\n"; }
关键注意事项
- 认准主窗口句柄:一个进程可能有多个窗口(比如弹窗、子控件),只操作可见、可交互的顶层窗口,这是避免窗口冻住的核心(AutoIt大概率是误操作了后台子窗口)。
- 权限要匹配:如果目标程序是用管理员权限启动的,你的脚本也要以管理员身份运行,否则会出现权限不足无法操作的问题。
- 坐标规则:Windows的屏幕坐标以左上角为原点,x/y是窗口左上角的位置,width/height是窗口的整体尺寸(包含边框和标题栏)。
如果你对C++/VB.NET的示例也感兴趣,我也可以给你补个简单的C++示例,思路和上面完全一致~




