1

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

Duck Wizard
  • 61
  • 1
  • 8
  • Which line of code triggers the above error? Where do you place the *pynput* import (and how exactly does it look like) ? – CristiFati Dec 06 '18 at 13:31
  • Well, if i do call the asdf function at the end of it all, and with pynput imported at the top, right below importing ctypes, the error is triggered on the asdf function for the PressKey(0x11) within asdf, followed up with ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)). I'll edit the code quickly to show how it looks to replicate the error. – Duck Wizard Dec 06 '18 at 13:37
  • I am guessing that *pynput* also defines those structures but with slightly different names (case sensitivity differs). Then I also guessing that it also sets *argtypes* and *restype* for `ctypes.windll.user32.SendInput` to its own structs. If my guessing is right, you setting *argtypes* and *restype* for it to your structs, locally in your functions, should do the trick. Anyway, you should **always set them for any function before calling it**. https://stackoverflow.com/questions/52268294/python-ctypes-cdll-loadlibrary-instantiate-an-object-execute-its-method-priva/52272969#52272969. – CristiFati Dec 06 '18 at 13:49
  • Just out of curiosity, add `print(ctypes.windll.user32.SendInput.argtypes, ctypes.windll.user32.SendInput.restype)` just after `SendInput = ctypes.windll.user32.SendInput `. – CristiFati Dec 06 '18 at 13:56
  • (, , ) Yup, you're right. pynput did screw with it. How would i go on about setting the argtypes and restypes? – Duck Wizard Dec 06 '18 at 13:58

2 Answers2

2

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:

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • Wow! Thank you so much. I guess i sort of wasted my time studying up on c++. Wasn't hard to compare python and it at all though, so it seem i could probably pick up on it like nothing. Even spent a bunch of time trying to figure out how ctypes works. Again, thank you very much. – Duck Wizard Dec 06 '18 at 15:11
  • Getting knowledge is never a waste of time (as it will probably come in handy later). You're welcome! – CristiFati Dec 06 '18 at 15:25
2

This question was linked from here and made me aware of the issue.

For anybody still encountering this issue, the master branch of pynput has been updated to not use internal types in the argtypes calls, but rather a generic ctypes.c_voidp, which should be compatible with whatever you pass.