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

Python自动化开发:屏蔽用户键鼠输入同时保留pynput热键功能的方法

解决BlockInput屏蔽用户输入时无法用pynput发送热键的问题

BlockInput会全局屏蔽所有输入事件,包括程序模拟的键鼠操作,这就是你遇到问题的核心原因。要实现只屏蔽用户物理输入、允许pynput模拟热键的需求,可以通过Windows的低级钩子(Low-Level Hooks)来精准过滤输入:

核心思路

Windows的低级键盘/鼠标钩子可以捕获系统内所有输入事件,我们可以通过事件的flags字段区分两类输入:

  • 用户物理触发的输入,其flags不会包含LLKHF_INJECTED(键盘)或LLMHF_INJECTED(鼠标)标志
  • pynput模拟的输入会自带该标志,只需放行这类事件、拦截物理输入即可

完整代码示例

import ctypes
import threading
from pynput.keyboard import Key, Controller

# 定义钩子所需的Windows结构体
class KBDLLHOOKSTRUCT(ctypes.Structure):
    _fields_ = [
        ("vkCode", ctypes.c_uint),
        ("scanCode", ctypes.c_uint),
        ("flags", ctypes.c_uint),
        ("time", ctypes.c_uint),
        ("dwExtraInfo", ctypes.c_void_p)
    ]

class MSLLHOOKSTRUCT(ctypes.Structure):
    _fields_ = [
        ("pt", ctypes.c_long * 2),
        ("mouseData", ctypes.c_uint),
        ("flags", ctypes.c_uint),
        ("time", ctypes.c_uint),
        ("dwExtraInfo", ctypes.c_void_p)
    ]

# 键盘钩子回调:拦截物理输入,放行模拟输入
def keyboard_hook_proc(nCode, wParam, lParam):
    if nCode >= 0:
        kb_data = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
        # LLKHF_INJECTED = 0x10,无此标志则为物理输入
        if not (kb_data.flags & 0x10):
            return 1  # 拦截输入
    # 传递其他事件给下一个钩子
    return ctypes.windll.user32.CallNextHookEx(0, nCode, wParam, lParam)

# 鼠标钩子回调:拦截物理输入,放行模拟输入
def mouse_hook_proc(nCode, wParam, lParam):
    if nCode >= 0:
        ms_data = ctypes.cast(lParam, ctypes.POINTER(MSLLHOOKSTRUCT)).contents
        # LLMHF_INJECTED = 0x10,无此标志则为物理输入
        if not (ms_data.flags & 0x10):
            return 1  # 拦截输入
    # 传递其他事件给下一个钩子
    return ctypes.windll.user32.CallNextHookEx(0, nCode, wParam, lParam)

# 启动钩子并维持消息循环
def start_input_block():
    # 设置全局低级键盘钩子
    keyboard_hook = ctypes.windll.user32.SetWindowsHookExA(
        13,  # WH_KEYBOARD_LL
        keyboard_hook_proc,
        ctypes.windll.kernel32.GetModuleHandleA(None),
        0
    )
    # 设置全局低级鼠标钩子
    mouse_hook = ctypes.windll.user32.SetWindowsHookExA(
        14,  # WH_MOUSE_LL
        mouse_hook_proc,
        ctypes.windll.kernel32.GetModuleHandleA(None),
        0
    )

    # 运行消息循环以维持钩子生效
    msg = ctypes.c_int()
    while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
        ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
        ctypes.windll.user32.DispatchMessage(ctypes.byref(msg))

    # 任务结束后卸载钩子
    ctypes.windll.user32.UnhookWindowsHookEx(keyboard_hook)
    ctypes.windll.user32.UnhookWindowsHookEx(mouse_hook)

# 自动化任务:发送F12热键
def automation_task():
    keyboard = Controller()
    # 等待钩子线程初始化完成
    threading.Event().wait(0.5)
    # 执行热键操作
    keyboard.press(Key.f12)
    keyboard.release(Key.f12)
    # 发送退出信号终止钩子消息循环
    ctypes.windll.user32.PostQuitMessage(0)

if __name__ == "__main__":
    # 启动钩子线程
    hook_thread = threading.Thread(target=start_input_block)
    hook_thread.start()
    # 启动自动化任务线程
    task_thread = threading.Thread(target=automation_task)
    task_thread.start()
    # 等待所有线程结束
    hook_thread.join()
    task_thread.join()

注意事项

  • 程序需要以管理员权限运行,否则无法成功设置全局低级钩子
  • 钩子的消息循环必须持续运行才能维持拦截状态,示例中用线程分离了钩子逻辑和自动化任务
  • 若需要长期屏蔽输入,可调整消息循环的退出条件,避免任务结束后立即解除拦截

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:40:29