Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Python] 模拟键盘输入 #40

Open
yangruihan opened this issue Jun 15, 2021 · 0 comments
Open

[Python] 模拟键盘输入 #40

yangruihan opened this issue Jun 15, 2021 · 0 comments

Comments

@yangruihan
Copy link
Owner

Python 模拟键盘输入

一、一般应用

使用autopy即可

官方例子

import autopy

autopy.key.tap(autopy.key.Code.TAB, [autopy.key.Modifier.META])
autopy.key.tap("w", [autopy.key.Modifier.META])

二、游戏

由于很多游戏是直接使用DirectInput,使用上述提到的autopy很可能没有效果

需要使用ScanCodes而不是VKs模拟输入

代码如下:

import ctypes
import time

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( 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))

# directx scan codes http://www.gamespp.com/directx/directInputKeyboardScanCodes.html
while (True):
    PressKey(0x11)
    time.sleep(1)
    ReleaseKey(0x11)
    time.sleep(1)

注意:

  • 模拟方向键dwFlags要加上0x0001

三、参考链接

四、完整工具代码

input_utils.py

import ctypes
import time

"""
参考链接:
https://stackoverflow.com/questions/14489013/simulate-python-keypresses-for-controlling-a-game
https://gist.github.com/tracend/912308
https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/ns-winuser-keybdinput?redirectedfrom=MSDN
"""

SendInput = ctypes.windll.user32.SendInput

DIK_ESCAPE = 0x01
DIK_1 = 0x02
DIK_2 = 0x03
DIK_3 = 0x04
DIK_4 = 0x05
DIK_5 = 0x06
DIK_6 = 0x07
DIK_7 = 0x08
DIK_8 = 0x09
DIK_9 = 0x0A
DIK_0 = 0x0B
DIK_MINUS = 0x0C
DIK_EQUALS = 0x0D
DIK_BACK = 0x0E
DIK_TAB = 0x0F
DIK_Q = 0x10
DIK_W = 0x11
DIK_E = 0x12
DIK_R = 0x13
DIK_T = 0x14
DIK_Y = 0x15
DIK_U = 0x16
DIK_I = 0x17
DIK_O = 0x18
DIK_P = 0x19
DIK_LBRACKET = 0x1A
DIK_RBRACKET = 0x1B
DIK_RETURN = 0x1C
DIK_LCONTROL = 0x1D
DIK_A = 0x1E
DIK_S = 0x1F
DIK_D = 0x20
DIK_F = 0x21
DIK_G = 0x22
DIK_H = 0x23
DIK_J = 0x24
DIK_K = 0x25
DIK_L = 0x26
DIK_SEMICOLON = 0x27
DIK_APOSTROPHE = 0x28
DIK_GRAVE = 0x29
DIK_LSHIFT = 0x2A
DIK_BACKSLASH = 0x2B
DIK_Z = 0x2C
DIK_X = 0x2D
DIK_C = 0x2E
DIK_V = 0x2F
DIK_B = 0x30
DIK_N = 0x31
DIK_M = 0x32
DIK_COMMA = 0x33
DIK_PERIOD = 0x34
DIK_SLASH = 0x35
DIK_RSHIFT = 0x36
DIK_MULTIPLY = 0x37
DIK_LMENU = 0x38
DIK_SPACE = 0x39
DIK_CAPITAL = 0x3A
DIK_F1 = 0x3B
DIK_F2 = 0x3C
DIK_F3 = 0x3D
DIK_F4 = 0x3E
DIK_F5 = 0x3F
DIK_F6 = 0x40
DIK_F7 = 0x41
DIK_F8 = 0x42
DIK_F9 = 0x43
DIK_F10 = 0x44
DIK_NUMLOCK = 0x45
DIK_SCROLL = 0x46
DIK_NUMPAD7 = 0x47
DIK_NUMPAD8 = 0x48
DIK_NUMPAD9 = 0x49
DIK_SUBTRACT = 0x4A
DIK_NUMPAD4 = 0x4B
DIK_NUMPAD5 = 0x4C
DIK_NUMPAD6 = 0x4D
DIK_ADD = 0x4E
DIK_NUMPAD1 = 0x4F
DIK_NUMPAD2 = 0x50
DIK_NUMPAD3 = 0x51
DIK_NUMPAD0 = 0x52
DIK_DECIMAL = 0x53
DIK_OEM_102 = 0x56
DIK_F11 = 0x57
DIK_F12 = 0x58
DIK_F13 = 0x64
DIK_F14 = 0x65
DIK_F15 = 0x66
DIK_KANA = 0x70
DIK_ABNT_C1 = 0x73
DIK_CONVERT = 0x79
DIK_NOCONVERT = 0x7B
DIK_YEN = 0x7D
DIK_ABNT_C2 = 0x7E
DIK_NUMPADEQUALS = 0x8D
DIK_PREVTRACK = 0x90
DIK_AT = 0x91
DIK_COLON = 0x92
DIK_UNDERLINE = 0x93
DIK_KANJI = 0x94
DIK_STOP = 0x95
DIK_AX = 0x96
DIK_UNLABELED = 0x97
DIK_NEXTTRACK = 0x99
DIK_NUMPADENTER = 0x9C
DIK_RCONTROL = 0x9D
DIK_MUTE = 0xA0
DIK_CALCULATOR = 0xA1
DIK_PLAYPAUSE = 0xA2
DIK_MEDIASTOP = 0xA4
DIK_VOLUMEDOWN = 0xAE
DIK_VOLUMEUP = 0xB0
DIK_WEBHOME = 0xB2
DIK_NUMPADCOMMA = 0xB3
DIK_DIVIDE = 0xB5
DIK_SYSRQ = 0xB7
DIK_RMENU = 0xB8
DIK_PAUSE = 0xC5
DIK_HOME = 0xC7
DIK_UP = 0xC8
DIK_PRIOR = 0xC9
DIK_LEFT = 0xCB
DIK_RIGHT = 0xCD
DIK_END = 0xCF
DIK_DOWN = 0xD0
DIK_NEXT = 0xD1
DIK_INSERT = 0xD2
DIK_DELETE = 0xD3
DIK_LWIN = 0xDB
DIK_RWIN = 0xDC
DIK_APPS = 0xDD
DIK_POWER = 0xDE
DIK_SLEEP = 0xDF
DIK_WAKE = 0xE3
DIK_WEBSEARCH = 0xE5
DIK_WEBFAVORITES = 0xE6
DIK_WEBREFRESH = 0xE7
DIK_WEBSTOP = 0xE8
DIK_WEBFORWARD = 0xE9
DIK_WEBBACK = 0xEA
DIK_MYCOMPUTER = 0xEB
DIK_MAIL = 0xEC
DIK_MEDIASELECT = 0xED

KeyMap = {
    "DIK_ESCAPE": 0x01,
    "DIK_1": 0x02,
    "DIK_2": 0x03,
    "DIK_3": 0x04,
    "DIK_4": 0x05,
    "DIK_5": 0x06,
    "DIK_6": 0x07,
    "DIK_7": 0x08,
    "DIK_8": 0x09,
    "DIK_9": 0x0A,
    "DIK_0": 0x0B,
    "DIK_MINUS": 0x0C,
    "DIK_EQUALS": 0x0D,
    "DIK_BACK": 0x0E,
    "DIK_TAB": 0x0F,
    "DIK_Q": 0x10,
    "DIK_W": 0x11,
    "DIK_E": 0x12,
    "DIK_R": 0x13,
    "DIK_T": 0x14,
    "DIK_Y": 0x15,
    "DIK_U": 0x16,
    "DIK_I": 0x17,
    "DIK_O": 0x18,
    "DIK_P": 0x19,
    "DIK_LBRACKET": 0x1A,
    "DIK_RBRACKET": 0x1B,
    "DIK_RETURN": 0x1C,
    "DIK_LCONTROL": 0x1D,
    "DIK_A": 0x1E,
    "DIK_S": 0x1F,
    "DIK_D": 0x20,
    "DIK_F": 0x21,
    "DIK_G": 0x22,
    "DIK_H": 0x23,
    "DIK_J": 0x24,
    "DIK_K": 0x25,
    "DIK_L": 0x26,
    "DIK_SEMICOLON": 0x27,
    "DIK_APOSTROPHE": 0x28,
    "DIK_GRAVE": 0x29,
    "DIK_LSHIFT": 0x2A,
    "DIK_BACKSLASH": 0x2B,
    "DIK_Z": 0x2C,
    "DIK_X": 0x2D,
    "DIK_C": 0x2E,
    "DIK_V": 0x2F,
    "DIK_B": 0x30,
    "DIK_N": 0x31,
    "DIK_M": 0x32,
    "DIK_COMMA": 0x33,
    "DIK_PERIOD": 0x34,
    "DIK_SLASH": 0x35,
    "DIK_RSHIFT": 0x36,
    "DIK_MULTIPLY": 0x37,
    "DIK_LMENU": 0x38,
    "DIK_SPACE": 0x39,
    "DIK_CAPITAL": 0x3A,
    "DIK_F1": 0x3B,
    "DIK_F2": 0x3C,
    "DIK_F3": 0x3D,
    "DIK_F4": 0x3E,
    "DIK_F5": 0x3F,
    "DIK_F6": 0x40,
    "DIK_F7": 0x41,
    "DIK_F8": 0x42,
    "DIK_F9": 0x43,
    "DIK_F10": 0x44,
    "DIK_NUMLOCK": 0x45,
    "DIK_SCROLL": 0x46,
    "DIK_NUMPAD7": 0x47,
    "DIK_NUMPAD8": 0x48,
    "DIK_NUMPAD9": 0x49,
    "DIK_SUBTRACT": 0x4A,
    "DIK_NUMPAD4": 0x4B,
    "DIK_NUMPAD5": 0x4C,
    "DIK_NUMPAD6": 0x4D,
    "DIK_ADD": 0x4E,
    "DIK_NUMPAD1": 0x4F,
    "DIK_NUMPAD2": 0x50,
    "DIK_NUMPAD3": 0x51,
    "DIK_NUMPAD0": 0x52,
    "DIK_DECIMAL": 0x53,
    "DIK_OEM_102": 0x56,
    "DIK_F11": 0x57,
    "DIK_F12": 0x58,
    "DIK_F13": 0x64,
    "DIK_F14": 0x65,
    "DIK_F15": 0x66,
    "DIK_KANA": 0x70,
    "DIK_ABNT_C1": 0x73,
    "DIK_CONVERT": 0x79,
    "DIK_NOCONVERT": 0x7B,
    "DIK_YEN": 0x7D,
    "DIK_ABNT_C2": 0x7E,
    "DIK_NUMPADEQUALS": 0x8D,
    "DIK_PREVTRACK": 0x90,
    "DIK_AT": 0x91,
    "DIK_COLON": 0x92,
    "DIK_UNDERLINE": 0x93,
    "DIK_KANJI": 0x94,
    "DIK_STOP": 0x95,
    "DIK_AX": 0x96,
    "DIK_UNLABELED": 0x97,
    "DIK_NEXTTRACK": 0x99,
    "DIK_NUMPADENTER": 0x9C,
    "DIK_RCONTROL": 0x9D,
    "DIK_MUTE": 0xA0,
    "DIK_CALCULATOR": 0xA1,
    "DIK_PLAYPAUSE": 0xA2,
    "DIK_MEDIASTOP": 0xA4,
    "DIK_VOLUMEDOWN": 0xAE,
    "DIK_VOLUMEUP": 0xB0,
    "DIK_WEBHOME": 0xB2,
    "DIK_NUMPADCOMMA": 0xB3,
    "DIK_DIVIDE": 0xB5,
    "DIK_SYSRQ": 0xB7,
    "DIK_RMENU": 0xB8,
    "DIK_PAUSE": 0xC5,
    "DIK_HOME": 0xC7,
    "DIK_UP": 0xC8,
    "DIK_PRIOR": 0xC9,
    "DIK_LEFT": 0xCB,
    "DIK_RIGHT": 0xCD,
    "DIK_END": 0xCF,
    "DIK_DOWN": 0xD0,
    "DIK_NEXT": 0xD1,
    "DIK_INSERT": 0xD2,
    "DIK_DELETE": 0xD3,
    "DIK_LWIN": 0xDB,
    "DIK_RWIN": 0xDC,
    "DIK_APPS": 0xDD,
    "DIK_POWER": 0xDE,
    "DIK_SLEEP": 0xDF,
    "DIK_WAKE": 0xE3,
    "DIK_WEBSEARCH": 0xE5,
    "DIK_WEBFAVORITES": 0xE6,
    "DIK_WEBREFRESH": 0xE7,
    "DIK_WEBSTOP": 0xE8,
    "DIK_WEBFORWARD": 0xE9,
    "DIK_WEBBACK": 0xEA,
    "DIK_MYCOMPUTER": 0xEB,
    "DIK_MAIL": 0xEC,
    "DIK_MEDIASELECT": 0xED,
}

# 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)]


def getDIKKey(key):
    if f"DIK_{key}" in KeyMap:
        return KeyMap[f"DIK_{key}"]

    return 0

def getDIKParam(key):
    if key in ["Left", "Right", "Up", "Down"]:
        return getDIKKey(key.upper()), 0x0001
    return getDIKKey(key), 0


def pressKey(hexKeyCode, flags=0):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput(0, hexKeyCode, 0x0008 | flags, 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, flags=0):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput(
        0, hexKeyCode, 0x0008 | 0x0002 | flags, 0, ctypes.pointer(extra)
    )
    x = Input(ctypes.c_ulong(1), ii_)
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant