python-3.xdirectxctypesdirectinputpynput

How to keep pynput and ctypes from clashing?


I'm using this gem from somewhere on this site.

import ctypes
import pynput


SendInput = ctypes.windll.user32.SendInput

W = 0x11
A = 0x1E
S = 0x1F
D = 0x20

# 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
# ganna need to rework pynput for this to work
import time


def asdf():
    while True:
        PressKey(0x11)
        time.sleep(1)
        ReleaseKey(0x11)
        time.sleep(1)

asdf()

But by just having pynput imported, returns this error.

ctypes.ArgumentError: argument 2: : expected LP_INPUT instance instead of LP_Input

This little ctypes script does work, standalone, but i really want to try to incorporate these mechanics into the rest of my program. I don't want to scrap the pynput part of my code. It's gotten pretty big.

Is there some way to keep them from trying to work with each other? Because i think it's because pynput works more like a wrapper and is sort of augmenting the data it pulls. I don't know exactly, still learning.

The reason why i need ctypes is because it's the only solution i found that outputs direct input.(Works with games and whatever things that uses directx.) Sorry if this isn't enough info or if i posted this in an ugly way. Am willing to fix this question through suggestions.

Update:

Going to learn c.

heres the rest of the error.

Traceback (most recent call last): File "C:/Users/bbdan/PycharmProjects/Playground/directkeys.py", line 72, in asdf() File "C:/Users/bbdan/PycharmProjects/Playground/directkeys.py", line 67, in asdf PressKey(0x11) File "C:/Users/bbdan/PycharmProjects/Playground/directkeys.py", line 50, in PressKey ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) ctypes.ArgumentError: argument 2: : expected LP_INPUT instance instead of LP_Input


Solution

  • I pip install input, and played a bit with it. My guessing was right, Pynput defines those structures, but with slightly different names, and sets argtypes (and restype) for ctypes.windll.user32.SendInput to its own definitions.
    That's why when you try to supply instances of your structures, it sees it complains about type mismatch.

    There are a number of solutions to fix this. Anyway, the simplest one was to simply replace your structs (you don't need them anymore) with the Pynput ones.

    Note: It's just a dumb replace, things can be organized a lot nicer, and I'm sure that Pynput has a mechanism of its own to achieve this, in order to spare the user of writing this code.

    The 2 modified versions of PressKey and ReleaseKey:

    def PressKeyPynput(hexKeyCode):
        extra = ctypes.c_ulong(0)
        ii_ = pynput._util.win32.INPUT_union()
        ii_.ki = pynput._util.win32.KEYBDINPUT(0, hexKeyCode, 0x0008, 0, ctypes.cast(ctypes.pointer(extra), ctypes.c_void_p))
        x = pynput._util.win32.INPUT(ctypes.c_ulong(1), ii_)
        SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
    
    def ReleaseKeyPynput(hexKeyCode):
        extra = ctypes.c_ulong(0)
        ii_ = pynput._util.win32.INPUT_union()
        ii_.ki = pynput._util.win32.KEYBDINPUT(0, hexKeyCode, 0x0008 | 0x0002, 0, ctypes.cast(ctypes.pointer(extra), ctypes.c_void_p))
        x = pynput._util.win32.INPUT(ctypes.c_ulong(1), ii_)
        SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
    

    Also check: