Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Python to send keystrokes to games in Windows?

I've been working with Python in a Windows environment and I wrote a script to automate some tasks in a known game. The task involves heavy use of both mouse and keyboard inputs.

Said script, however, has only one issue: it cannot send keystrokes to the application. I've tried at least 3 different methods that I shall post below and some variations (also read tenths of similar questions/answers, to no avail)

First one, using the win32api module:

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

Nothing special about it, works perfectly (a "f" is typed) in any text editor, browser... However, if I open a game like, say, Counter-Strike, then the keystroke gets "lost" - as in, nothing occurs. On the other hand, if I open Counter-Strike's console, then the keystroke gets registered (like in notepad). Tested in another game, League of Legends, exactly the same behaviour. In the actual game, no keystroke is detected. If, however, I open the chat (still ingame) and re-run the script then it gets registered by the chat.

Onto the second method:

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

Exactly the same behaviour as above. Works fine in everything but the game, and in it only works in chats.

Third method (credit goes to whoever posted it in another stackoverflow thread), more advanced (calling SendInput()) with the ctypes module. In theory, of the three, this one is the closest to simulate an actual, physical key press:

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

... it does not work either. Oddly enough, it displays the exact same behaviour as the previous three: works in any text editor/simple application, gets ignored by games or is only registered in the game chat section.

If I were to guess I'd say these games are getting their keyboard events in some other way that I've not covered with any of these 3 methods, thus ignoring these ones.

I'd appreciate any help. If possible, with concrete examples of code working in either CS, LoL or similar games so that I have a starting point.

like image 297
Daniel Shaw Avatar asked Apr 12 '14 00:04

Daniel Shaw


2 Answers

Your games probably use DirectInput. Try DirectInput scancodes with either of those methods. A quick googling gives this website: http://www.gamespp.com/directx/directInputKeyboardScanCodes.html

like image 86
Gil D. Avatar answered Oct 06 '22 13:10

Gil D.


You could try enumerating the application's windows using EnumWindows() and call SendMessage() to the main windows of the game directly.

like image 22
shoosh Avatar answered Oct 06 '22 15:10

shoosh