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

如何通过Python Win32模块锁定外部窗口大小与位置

完全可以实现,无需注入第三方程序进程,通过Windows标准窗口API即可完成尺寸、位置的锁定,适配Python3.x、Win10、管理员权限的运行环境。

实现原理
  • 锁定尺寸:Windows窗口默认的可拖拽调整边框能力由窗口样式位控制,移除WS_THICKFRAME(即WS_SIZEBOX)样式和最大化按钮样式后,系统会默认禁用边框拖拽调整、最大化改尺寸的能力。针对自定义自绘边框的特殊程序,增加定时校验兜底,只要检测到窗口尺寸偏离锁定值就立刻还原。
  • 锁定位置:通过兜底校验逻辑,检测窗口坐标偏移后立刻还原到初始锁定坐标,即可阻止用户通过拖拽标题栏、系统菜单移动指令修改窗口位置。
前置依赖

先安装pywin32库,执行命令:
pip install pywin32

完整示例代码
import win32gui
import win32con
import time
from threading import Thread

class WindowLocker:
    def __init__(self, hwnd):
        self.hwnd = hwnd
        self.locked = False
        # 记录锁定时的初始位置、尺寸作为固定基准值
        self.fixed_rect = win32gui.GetWindowRect(hwnd)
        self.lock_pos = False
        self.lock_size = False
        self.monitor_thread = None

    def _apply_size_style_lock(self):
        # 移除窗口可调整边框、最大化按钮样式
        style = win32gui.GetWindowLong(self.hwnd, win32con.GWL_STYLE)
        style &= ~(win32con.WS_THICKFRAME | win32con.WS_MAXIMIZEBOX)
        win32gui.SetWindowLong(self.hwnd, win32con.GWL_STYLE, style)
        # 刷新窗口框架让样式修改立即生效
        win32gui.SetWindowPos(
            self.hwnd, 0, 0, 0, 0, 0,
            win32con.SWP_NOMOVE | win32con.SWP_NOSIZE |
            win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED
        )

    def _monitor_loop(self):
        # 兜底校验循环,拦截绕过系统样式的调整操作
        left, top, right, bottom = self.fixed_rect
        fixed_w = right - left
        fixed_h = bottom - top
        while self.locked:
            if not win32gui.IsWindow(self.hwnd):
                break
            # 最小化状态不做调整,避免显示异常
            if win32gui.IsIconic(self.hwnd):
                time.sleep(0.1)
                continue
            cur_left, cur_top, cur_right, cur_bottom = win32gui.GetWindowRect(self.hwnd)
            cur_w = cur_right - cur_left
            cur_h = cur_bottom - cur_top
            need_reset = False
            new_left, new_top = cur_left, cur_top
            new_w, new_h = cur_w, cur_h

            if self.lock_size and (cur_w != fixed_w or cur_h != fixed_h):
                new_w = fixed_w
                new_h = fixed_h
                need_reset = True
            if self.lock_pos and (cur_left != left or cur_top != top):
                new_left = left
                new_top = top
                need_reset = True

            if need_reset:
                win32gui.MoveWindow(self.hwnd, new_left, new_top, new_w, new_h, True)
            time.sleep(0.05)

    def start_lock(self, lock_size=True, lock_pos=False):
        """
        启动窗口锁定
        :param lock_size: 是否锁定窗口尺寸
        :param lock_pos: 是否锁定窗口位置
        """
        if self.locked:
            return
        self.lock_size = lock_size
        self.lock_pos = lock_pos
        self.locked = True
        if lock_size:
            self._apply_size_style_lock()
        self.monitor_thread = Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()

    def stop_lock(self):
        """停止锁定,如需恢复窗口可调整样式可在此方法中补全样式还原逻辑"""
        self.locked = False
        if self.monitor_thread and self.monitor_thread.is_alive():
            self.monitor_thread.join(timeout=1)

if __name__ == "__main__":
    # 示例获取前台窗口句柄,实际使用替换为你持有的目标窗口HWND即可
    hwnd = win32gui.GetForegroundWindow()
    locker = WindowLocker(hwnd)
    # 同时锁定尺寸和位置,不需要锁位置可将lock_pos设为False
    locker.start_lock(lock_size=True, lock_pos=True)
    print("窗口已锁定,按Ctrl+C解除锁定")
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        locker.stop_lock()
        print("已解除窗口锁定")
使用说明
  • 代码默认通过win32gui.GetForegroundWindow()获取当前前台窗口作为锁定目标,实际使用时替换成你已经获取到的目标窗口HWND即可
  • 兜底校验轮询间隔设为50ms,CPU占用极低,同时能保证调整动作被立刻拦截,人眼不会感知到窗口移位或尺寸变化
  • 方案未使用全局钩子、进程注入类高风险操作,兼容性强,不会导致第三方程序崩溃,也不会触发杀毒软件告警
  • 如果需要在停止锁定后恢复窗口原本的可调整大小能力,可在stop_lock方法中添加逻辑,将之前移除的WS_THICKFRAME、WS_MAXIMIZEBOX样式位重新加回,再调用SetWindowPos刷新框架即可

内容的提问来源于stack exchange,提问作者Programer Beginner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 23:12:33