pywinauto send_keystrokes无法完全生效 如何扩展实现后台按键功能
实现方法
你需要的PressKey和ReleaseKey方法可以通过给pywinauto的win32窗口类做猴子补丁实现,不需要修改pywinauto源码。注意常规keybd_event接口只能给前台窗口发按键,要后台生效需要直接向目标窗口句柄投递键盘消息,不需要激活窗口。
首先在你的脚本开头加入以下补丁代码,整合ctypes调用Win32接口的逻辑:
import ctypes from ctypes import wintypes from pywinauto.controls.hwndwrapper import HwndWrapper # Win32常量定义 WM_KEYDOWN = 0x0100 WM_KEYUP = 0x0101 KEYEVENTF_KEYUP = 0x0002 MAPVK_VK_TO_VSC = 0 # 加载系统API user32 = ctypes.WinDLL('user32', use_last_error=True) # 显式定义API参数类型,避免64位环境调用异常 user32.PostMessageW.argtypes = (wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM) user32.PostMessageW.restype = wintypes.BOOL user32.MapVirtualKeyW.argtypes = (wintypes.UINT, wintypes.UINT) user32.MapVirtualKeyW.restype = wintypes.UINT user32.keybd_event.argtypes = (wintypes.BYTE, wintypes.BYTE, wintypes.DWORD, ctypes.c_void_p) def _build_key_lparam(vk_code, is_keyup=False): """构造键盘消息要求的lparam参数,和系统原生键盘事件参数格式对齐""" scan_code = user32.MapVirtualKeyW(vk_code, MAPVK_VK_TO_VSC) flag_bits = 0 if is_keyup: flag_bits |= 1 << 30 flag_bits |= 1 << 31 return (scan_code << 16) | flag_bits def press_key(self, vk_code, background=True): """ 按下指定虚拟键 :param vk_code: 按键虚拟键码,例如0x31对应主键盘数字1 :param background: 为True时后台向窗口投递消息,无需窗口前置;为False时用全局键盘事件,需要窗口在前台 """ if background: lparam = _build_key_lparam(vk_code, is_keyup=False) user32.PostMessageW(self.handle, WM_KEYDOWN, vk_code, lparam) else: scan_code = user32.MapVirtualKeyW(vk_code, MAPVK_VK_TO_VSC) user32.keybd_event(vk_code, scan_code, 0, 0) def release_key(self, vk_code, background=True): """ 抬起指定虚拟键 :param vk_code: 按键虚拟键码 :param background: 为True时后台向窗口投递消息,无需窗口前置;为False时用全局键盘事件,需要窗口在前台 """ if background: lparam = _build_key_lparam(vk_code, is_keyup=True) user32.PostMessageW(self.handle, WM_KEYUP, vk_code, lparam) else: scan_code = user32.MapVirtualKeyW(vk_code, MAPVK_VK_TO_VSC) user32.keybd_event(vk_code, scan_code, KEYEVENTF_KEYUP, 0) # 给pywinauto win32后端的窗口基类绑定方法 HwndWrapper.PressKey = press_key HwndWrapper.ReleaseKey = release_key
使用说明
- 补丁加载完成后,就可以按照你预期的方式直接调用方法,默认开启后台模式,不需要激活目标窗口:
from pywinauto import Application appWavie = Application(backend='win32').connect(handle=790164) Wavie = appWavie['Wavie'] VK_1 = 0x31 # 后台发送按键1的按下+抬起事件 Wavie.PressKey(VK_1) Wavie.ReleaseKey(VK_1)
- 针对你提到的无子窗口后台程序,PostMessage直接向顶层窗口发消息的模式兼容性远高于原生
send_keystrokes——后者默认会尝试查找焦点子控件,无控件的程序上会出现部分区域失效的问题。 - 如果遇到程序拦截窗口消息导致后台模式失效,可以切到前台模式,逻辑和ctypes实现的全局键盘事件完全一致:
Wavie.set_focus() Wavie.PressKey(VK_1, background=False) Wavie.ReleaseKey(VK_1, background=False)
注意:部分使用DirectInput读取输入的游戏、带反作弊的程序会拦截上述两种Win32层的按键模拟,这类场景需要驱动级输入方案,常规桌面程序都可以正常响应。
内容的提问来源于stack exchange,提问作者Wavilson Ferreira
相关产品推荐
相关产品推荐

