Эмуляция мыши и клавиатуры в окне игры через Python и pywin32
Многие разработчики автоматизации сталкиваются с проблемой: эмуляция движений мыши, кликов и нажатий клавиш через PostMessage и SendMessage отлично работает с обычными приложениями, но полностью игнорируется играми, такими как World of Tanks (Мир танков). В этой статье мы разберём причины такого поведения и предложим рабочие альтернативы.
Почему PostMessage не работает с играми?
Современные игры, особенно написанные на движках вроде Unity, Unreal Engine или собственных (как WoT), не обрабатывают стандартные сообщения Windows WM_MOUSEMOVE, WM_LBUTTONDOWN и WM_KEYDOWN напрямую. Вместо этого они используют низкоуровневый опрос состояния устройств ввода через DirectInput, Raw Input или XInput. PostMessage отправляет сообщение в очередь окна, но игра считывает состояние мыши и клавиатуры напрямую с драйверов, минуя эту очередь.
Основные причины неработоспособности
- Прямой опрос устройств: Игра использует
GetAsyncKeyState,GetRawInputDataили DirectInput, игнорируя очередь сообщений. - Античит-системы: WoT имеет встроенную защиту от скриптов, которая блокирует подозрительные сообщения.
- Фокус окна: Даже при
SetForegroundWindowиSetFocusигра может не принимать сообщения, если она не в активном режиме (например, свёрнута или заблокирована).
Альтернативные методы эмуляции ввода
Если PostMessage не срабатывает, рассмотрите следующие подходы:
1. SendInput (user32.dll)
Функция SendInput вставляет события в системный поток ввода, что воспринимается игрой как реальные действия пользователя. Недостаток - она требует активного окна и захватывает основной курсор.
import ctypes
from ctypes import wintypes
# Определение структуры INPUT
PUL = ctypes.POINTER(ctypes.c_ulong)
class MOUSEINPUT(ctypes.Structure):
_fields_ = [("dx", wintypes.LONG), ("dy", wintypes.LONG), ("mouseData", wintypes.DWORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", PUL)]
class INPUT(ctypes.Structure):
_fields_ = [("type", wintypes.DWORD), ("mi", MOUSEINPUT)]
# Пример клика левой кнопкой
def send_click(x, y):
ctypes.windll.user32.SetCursorPos(x, y)
ctypes.windll.user32.mouse_event(0x0002, 0, 0, 0, 0) # MOUSEEVENTF_LEFTDOWN
ctypes.windll.user32.mouse_event(0x0004, 0, 0, 0, 0) # MOUSEEVENTF_LEFTUP2. AutoHotkey через подпроцесс
Запустите скрипт AHK из Python с помощью subprocess. AHK умеет эмулировать ввод на уровне драйвера и работать с несколькими окнами через ControlClick и ControlSend.
import subprocess
def ahk_click(hwnd, x, y):
script = f'ControlClick, x{x} y{y}, ahk_id {hwnd}'
subprocess.run(['AutoHotkey.exe', script], shell=True)3. Interception или драйверные решения
Библиотека interception (pyinterception) позволяет перехватывать и подменять события ввода на уровне драйвера. Это самый надёжный метод для игр, но требует установки драйвера и прав администратора.
Практические советы для World of Tanks
Если вы работаете с Lesta GameCenter (СНГ-версия WoT), учтите:
- Игра использует собственный движок BigWorld, который опрашивает устройства через Raw Input.
- Попробуйте сначала активировать окно через
win32gui.SetForegroundWindow, затем сделать паузу 0.1-0.5 секунды и только потом отправлять события. - Для клавиш используйте
WM_KEYDOWNс правильным виртуальным кодом, но помните - игра может игнорировать сообщения, если окно не в фокусе.
Заключение
Эмуляция ввода в окно игры через PostMessage и SendMessage часто не работает из-за прямого опроса устройств. Для World of Tanks рекомендуем использовать SendInput с захватом курсора или запускать AHK-скрипты через подпроцесс. Если нужна работа с несколькими окнами одновременно, рассмотрите драйверные решения вроде Interception.