¿Usar Python para enviar pulsaciones de teclas a juegos en Windows?

He estado trabajando con Python en un entorno de Windows y escribí un script para automatizar algunas tareas en un juego conocido. La tarea implica un uso intensivo de las entradas del mouse y del teclado.

Sin embargo, dicho script solo tiene un problema: no puede enviar pulsaciones de teclas a la aplicación. He intentado al menos 3 métodos diferentes que publicaré a continuación y algunas variaciones (también lea décimas de preguntas / respuestas similares, en vano)

Primero, usando el módulo win32api :

 f = 0x46 # VirtualKey Code of the letter "F", see http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx win32api.keybd_event(f,0,0,0) # holds the "F" key down time.sleep(2) # waits 2 seconds win32api.keybd_event(f,0,win32con.KEYEVENTF_KEYUP,0) # releases the key 

Nada especial al respecto, funciona perfectamente (se escribe “f”) en cualquier editor de texto, navegador … Sin embargo, si abro un juego como, por ejemplo, Counter-Strike, entonces la pulsación de tecla se “pierde”, como en, no ocurre nada Por otro lado, si abro la consola de Counter-Strike, entonces se registra la pulsación de tecla (como en el bloc de notas). Probado en otro juego, League of Legends, exactamente el mismo comportamiento. En el juego real, no se detecta ninguna pulsación de tecla. Sin embargo, si abro el chat (aún dentro del juego ) y vuelvo a ejecutar el script, el chat lo registra.

En el segundo método:

 shell = win32com.client.Dispatch("WScript.Shell") shell.SendKeys("F") 

Exactamente el mismo comportamiento que el anterior. Funciona bien en todo menos en el juego, y en él solo funciona en chats.

Tercer método (el crédito es para quien lo haya publicado en otro hilo de stackoverflow), más avanzado (llamando a SendInput() ) con el módulo ctypes . En teoría, de los tres, este es el más cercano para simular una pulsación física real:

 SendInput = ctypes.windll.user32.SendInput # C struct redefinitions PUL = ctypes.POINTER(ctypes.c_ulong) class KeyBdInput(ctypes.Structure): _fields_ = [("wVk", ctypes.c_ushort), ("wScan", ctypes.c_ushort), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] class HardwareInput(ctypes.Structure): _fields_ = [("uMsg", ctypes.c_ulong), ("wParamL", ctypes.c_short), ("wParamH", ctypes.c_ushort)] class MouseInput(ctypes.Structure): _fields_ = [("dx", ctypes.c_long), ("dy", ctypes.c_long), ("mouseData", ctypes.c_ulong), ("dwFlags", ctypes.c_ulong), ("time",ctypes.c_ulong), ("dwExtraInfo", PUL)] class Input_I(ctypes.Union): _fields_ = [("ki", KeyBdInput), ("mi", MouseInput), ("hi", HardwareInput)] class Input(ctypes.Structure): _fields_ = [("type", ctypes.c_ulong), ("ii", Input_I)] # Actuals Functions def PressKey(hexKeyCode): extra = ctypes.c_ulong(0) ii_ = Input_I() ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0, 0, ctypes.pointer(extra) ) x = Input( ctypes.c_ulong(1), ii_ ) ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) def ReleaseKey(hexKeyCode): extra = ctypes.c_ulong(0) ii_ = Input_I() ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0x0002, 0, ctypes.pointer(extra) ) x = Input( ctypes.c_ulong(1), ii_ ) ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) def KeyPress(): PressKey(0x46) # press F time.sleep(.5) ReleaseKey(0x46) #release F 

… tampoco funciona. Por extraño que parezca, muestra exactamente el mismo comportamiento que los tres anteriores: funciona en cualquier editor de texto / aplicación simple, se ignora en los juegos o solo se registra en la sección de chat del juego.

Si tuviera que adivinar, diría que estos juegos están obteniendo sus eventos de teclado de alguna otra manera que no he cubierto con ninguno de estos 3 métodos, por lo tanto, ignorar estos.

Apreciaría cualquier ayuda. Si es posible, con ejemplos concretos de código que funciona en CS, LoL o juegos similares para tener un punto de partida.

Tus juegos probablemente usan DirectInput. Intente DirectInput scancodes con cualquiera de esos métodos. Un googling rápido da a este sitio web: http://www.gamespp.com/directx/directInputKeyboardScanCodes.html

Puedes intentar enumerar las ventanas de la aplicación usando EnumWindows() y llamar a SendMessage() directamente a las ventanas principales del juego.