Use Python to send keystrokes to games in Windows?
Asked Answered
W

2

12

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.

Walkin answered 12/4, 2014 at 0:12 Comment(1)
For browser based games, I found basic key press as you described above worked on Firefox browser, but did not work for chrome.Phalansterian
W
6

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

Widespread answered 5/10, 2014 at 17:44 Comment(0)
B
1

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

Boman answered 12/4, 2014 at 0:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.