pynput模拟按键部分程序无法识别的问题求助
问题:pynput模拟按键无法在vba-m等游戏程序中生效,Chrome/记事本正常
我用socket和pynput实现手机远程控制电脑程序,Chrome、记事本等普通程序能正常识别pynput模拟的按键,但vba-m(GBA模拟器)这类目标游戏程序完全没响应。已经尝试用管理员权限运行脚本,问题依然存在。
我的代码如下:
import socket,threading from pynput.keyboard import Key, Controller PORT=12345 SERVER="192.168.1.160" ADDR=(SERVER, PORT) FORMAT="utf-8" TERMINATED="ADIOS" server=socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(ADDR) conectados=[] conectadosaddr=[] keyboard = Controller() def start(): #Función que se encarga de iniciar el servidor y escuchar nuevas conexiones server.listen() print("Servidor iniciado") while True: conn,addr=server.accept() conectados.append(conn) conectadosaddr.append(addr) thread=threading.Thread(target=handle_client, args=(conn, addr)) thread.daemon=True thread.start() print(f"Conexiones activas: {len(conectados)}") def handle_client(conn, addr): #Función que se encarga de manejar las conexiones print(f"Nueva conexion de {addr}") connected=True while connected: try: data=conn.recv(1024).decode(FORMAT) if data: print(f"Recibido: {data} de {addr}") if data==TERMINATED: connected=False print("Se ha cerrado la conexion pacíficamente de "+str(addr)) elif data in "WASDZX": keyboard.press(data) keyboard.release(data) elif data=="BK": keyboard.press(Key.backspace) keyboard.release(Key.backspace) elif data=="ST": keyboard.press(Key.enter) keyboard.release(Key.enter) except Exception as e: connected=False print("ERROR: ",e) print(f"{addr} se ha desconectado") conectados.remove(conn) conectadosaddr.remove(addr) conn.close() if __name__ == '__main__': start()
解决方案
1. 改用pynput低级键盘API模拟硬件输入
普通的Controller模拟基于Windows消息队列,部分游戏会直接读取硬件输入状态,因此无法识别。改用pynput的低级API可以绕过这个限制:
修改代码导入部分和键盘实例:
import socket,threading from pynput.keyboard import Key, Controller, _win32 # ... 其他常量定义不变 keyboard = Controller() low_level_keyboard = _win32.Keyboard()
然后在handle_client函数中替换按键操作:
# 替换原来的按键处理逻辑 elif data in "WASDZX": key_code = _win32.KeyCode.from_char(data.upper()) low_level_keyboard.press(key_code) low_level_keyboard.release(key_code) elif data=="BK": low_level_keyboard.press(Key.backspace) low_level_keyboard.release(Key.backspace) elif data=="ST": low_level_keyboard.press(Key.enter) low_level_keyboard.release(Key.enter)
2. 直接调用Windows API的SendInput函数
如果低级API仍无效,可直接用ctypes调用SendInput,模拟最底层的硬件输入:
先添加以下工具函数:
import ctypes from ctypes import wintypes user32 = ctypes.WinDLL('user32', use_last_error=True) INPUT_KEYBOARD = 1 KEYEVENTF_KEYUP = 0x0002 MAPVK_VK_TO_VSC = 0 class KEYBDINPUT(ctypes.Structure): _fields_ = (("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.WPARAM)) class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): _fields_ = (("ki", KEYBDINPUT),) _anonymous_ = ("_input",) _fields_ = (("type", wintypes.DWORD), ("_input", _INPUT)) def press_key(key): if isinstance(key, str): vk = ord(key.upper()) else: vk = key.value.vk scan_code = user32.MapVirtualKeyW(vk, MAPVK_VK_TO_VSC) # 模拟按下 input_down = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=vk, wScan=scan_code, dwFlags=0)) user32.SendInput(1, ctypes.byref(input_down), ctypes.sizeof(INPUT)) # 模拟释放 input_up = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=vk, wScan=scan_code, dwFlags=KEYEVENTF_KEYUP)) user32.SendInput(1, ctypes.byref(input_up), ctypes.sizeof(INPUT))
然后在handle_client中替换按键操作:
elif data in "WASDZX": press_key(data) elif data=="BK": press_key(Key.backspace) elif data=="ST": press_key(Key.enter)
3. 确保游戏窗口处于前台
部分游戏仅在前台时响应输入,可在模拟按键前激活vba-m窗口:
用pygetwindow实现(需先安装:pip install pygetwindow):
import pygetwindow as gw # 在handle_client的按键处理前添加 window_list = gw.getWindowsWithTitle("VisualBoyAdvance-M") if window_list: window_list[0].activate()
或者用ctypes直接调用Windows API:
user32 = ctypes.WinDLL('user32') hwnd = user32.FindWindowW(None, "VisualBoyAdvance-M") if hwnd: user32.SetForegroundWindow(hwnd)
内容的提问来源于stack exchange,提问作者FerdinandoPH
相关产品推荐
相关产品推荐

