如何用pywinauto在游戏中模拟按键

6 投票
2 回答
9204 浏览
提问于 2025-04-19 18:40

我已经尝试了各种方法整整一年了。我在Python方面还是个初学者,刚完成了Project Euler的前两个题目。

我试过几种方法,想在我玩的游戏中模拟按键。我可以很轻松地用autohotkey和宏键盘/鼠标来做到这一点。不过,我想通过Python或C语言来实现这个功能。

我猜测win32 API在视频游戏中可能被忽略,所以我需要通过Direct X来模拟按键。

谢谢大家的帮助。这是我最近的尝试……结果失败了。

每次我运行新的游戏实例时,都需要重新获取或更改句柄。

我模拟的按键在浏览器和记事本中可以正常工作,但在游戏中却不行。这里的“不行”是指没有用户输入。

下面的代码可以切换到游戏窗口,但无法模拟用户的输入。

import pywinauto
import time
from pywinauto import application
app = application.Application()
app.connect_(handle = 0x14002a)
dialogs = app.windows_(handle = 0x14002a)
dlg = app.top_window_()
time.sleep(1)
app.MapleStory.TypeKeys("%A")
time.sleep(1)
app.MapleStory.TypeKeys("%A")
time.sleep(1)
app.MapleStory.TypeKeys("%A")
time.sleep(1)
app.MapleStory.TypeKeys("%A")
time.sleep(1)
app.MapleStory.TypeKeys("%A")
time.sleep(1)
app.MapleStory.TypeKeys("%A")

2 个回答

1

我不知道这是不是太晚了,但有些游戏确实可以通过使用扫描码来模拟鼠标和键盘事件,而不是用虚拟键,方法是用SendInput。

不过,具体到《冒险岛》这款游戏,它对虚拟键和扫描码都没有反应。

我知道的在这个游戏里模拟键盘事件的唯一方法是使用内核驱动。一个例子是使用WinIo直接写入i8042和i8048控制器。8042上的命令0xD2专门用来模拟硬件级别的输入。不过,WinIo会打开一个很大的安全漏洞,因为它直接把硬件端口暴露给用户空间的程序,所以我不能说我推荐这个方法。

5

我过去的自己,我们经历了很长的旅程。虽然我们还有很多要学习的,但这是我们发现的:

如果游戏是在DirectX上运行的,发送虚拟按键会被忽略。你应该使用sendinput,并发送扫描码。

# http://www.gamespp.com/directx/directInputKeyboardScanCodes.html

import ctypes
import time

SendInput = ctypes.windll.user32.SendInput

W = 0x11
A = 0x1E
S = 0x1F
D = 0x20
Z = 0x2C
UP = 0xC8
DOWN = 0xD0
LEFT = 0xCB
RIGHT = 0xCD
ENTER = 0x1C 

# 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( 0, hexKeyCode, 0x0008, 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( 0, hexKeyCode, 0x0008 | 0x0002, 0, 
ctypes.pointer(extra) )
    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

if __name__ == '__main__':
    pressKey(0x11)
    time.sleep(1)
    releaseKey(0x11)
    time.sleep(1)

来源: 模拟Python按键以控制游戏 http://www.gamespp.com/directx/directInputKeyboardScanCodes.html

感谢Sentdex提供的精彩机器学习教程。 我一直想对我喜欢的一些游戏做这个,但由于DirectX的原因,无法发送按键。

下一步: 使用Windows驱动程序工具包来模拟按键...

撰写回答