Использовать Python для отправки нажатий клавиш в игры в Windows?

Я работал с Python в среде Windows, и я написал script для автоматизации некоторых задач в известной игре. Задача состоит в интенсивном использовании как входов мыши, так и клавиатуры.

Указанный script, однако, имеет только одну проблему: он не может отправлять нажатия клавиш в приложение. Я пробовал как минимум 3 разных метода, которые я буду размещать ниже, и некоторые варианты (также читаем десятки аналогичных вопросов/ответов, безрезультатно)

Сначала, используя модуль 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

Ничего особенного в этом нет, работает отлично (набирается "f" ) в любом текстовом редакторе, браузере... Однако, если я открою игру, например, Counter-Strike, то нажатие клавиши "потеряно" - как и в, ничего не происходит. С другой стороны, если я открываю консоль Counter-Strike, то нажатие клавиши получает (например, в блокноте). Испытано в другой игре "Лига Легенд", точно такое же поведение. В реальной игре не обнаружено нажатия клавиши. Если, однако, я открываю чат (еще ingame) и снова запускаю script, после чего он регистрируется в чате.

К второму методу:

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

Точно так же, как и выше. Работает отлично во всем, кроме игры, и в нем работает только в чатах.

Третий метод (кредит принадлежит тому, кто отправил его в другой поток stackoverflow), более продвинутый (вызов SendInput()) с модулем ctypes. Теоретически, из трех, этот наиболее близок к симуляции фактического физического нажатия клавиши:

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

... он тоже не работает. Как ни странно, он показывает точное то же поведение, что и предыдущие три: работает в любом текстовом редакторе/простом приложении, игнорируется играми или зарегистрируется только в разделе игрового чата.

Если бы я догадался, я бы сказал, что эти игры получают свои события на клавиатуре каким-то другим способом, что я не охватил ни один из этих 3 методов, таким образом игнорируя эти.

Буду признателен за любую помощь. Если возможно, с конкретными примерами кода, работающими в CS, LoL или подобных играх, чтобы у меня была начальная точка.

Ответ 2

Вы можете попробовать перечислить окна приложений с помощью EnumWindows() и вызвать SendMessage() в главные окна игры напрямую.