I am trying to get the SendInput function from user32.dll to work in python using ctypes.I am a noob but from what I read from the docs you have to create the structs the function requires in python and then pass it to the function.
import ctypes
import keyboard
from ctypes import *
lib = windll.user32
KEYEVENTF_SCANCODE = 0x8
KEYEVENTF_KEYUP = 0x2
SPACEBAR = 57 # 0x39
INPUT_KEYBOARD = 1
class KEYBDINPUT(Structure):
_fields_ = [('wVk' , c_ushort) , ('wScan' , c_ushort)
, ('dwFlags' , c_ulong) , ('time' , c_ulong) , ('dwExtraInfo' , c_ulong)]
class INPUT(Structure):
_fields_ = [('type' , c_ulong) ,('ki' , KEYBDINPUT)]
lib.SendInput.restype = c_uint
lib.SendInput.argtypes = [c_uint , INPUT , c_int]
keybdinput_obj = KEYBDINPUT(0 , SPACEBAR , KEYEVENTF_SCANCODE , 0 , 0)
input_obj = INPUT(INPUT_KEYBOARD , keybdinput_obj)
keyboard.wait('u')
lib.SendInput(1 , byref(input_obj) , sizeof(INPUT))
keybdinput_obj = KEYBDINPUT(0 , SPACEBAR , KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP , 0 , 0)
input_obj = INPUT(INPUT_KEYBOARD , keybdinput_obj)
lib.SendInput(1 , byref(input_obj) , sizeof(INPUT))
In the microsoft docs at which I guided myself from the INPUT struct had an union but i figured if I would only need the KEYBDINPUT then it's the same thing as if i had an union.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
I pretty much got stuck because i can't see what's going wrong here so I am asking for help.The program is supposed to send a space after i press 'u' on the keyboard (this was for debugging purposes) and i do it this way because i want it to send as a scancode instead of a virtual keypress.
So if there is another way in python with with which you can send scancodes , that'll work and I would appreciate it a lot .
Define the whole union, or at least MOUSEINPUT, which is the largest member of the union. You can test that your definition is the correct size by print(sizeof(INPUT)) and it should agree with printing the size of an INPUT structure in a C program. I got size 28 for 32-bit and 40 for 64-bit C structure.
Also, your second parameter to SendInput is POINTER(INPUT), not INPUT, and ULONG_PTR is not necessarily c_ulong because it depends on running 32-bit or 64-bit Python.
Here's a tested example:
import ctypes as ct
import ctypes.wintypes as w
import struct
import time
import array
KEYEVENTF_SCANCODE = 0x8
KEYEVENTF_UNICODE = 0x4
KEYEVENTF_KEYUP = 0x2
SPACE = 0x39
INPUT_KEYBOARD = 1
# not defined by wintypes
ULONG_PTR = ct.c_size_t
class KEYBDINPUT(ct.Structure):
_fields_ = [('wVk' , w.WORD),
('wScan', w.WORD),
('dwFlags', w.DWORD),
('time', w.DWORD),
('dwExtraInfo', ULONG_PTR)]
class MOUSEINPUT(ct.Structure):
_fields_ = [('dx' , w.LONG),
('dy', w.LONG),
('mouseData', w.DWORD),
('dwFlags', w.DWORD),
('time', w.DWORD),
('dwExtraInfo', ULONG_PTR)]
class HARDWAREINPUT(ct.Structure):
_fields_ = [('uMsg' , w.DWORD),
('wParamL', w.WORD),
('wParamH', w.WORD)]
class DUMMYUNIONNAME(ct.Union):
_fields_ = [('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT)]
class INPUT(ct.Structure):
_anonymous_ = ['u']
_fields_ = [('type', w.DWORD),
('u', DUMMYUNIONNAME)]
print(ct.sizeof(INPUT))
def zerocheck(result, func, args):
if result == 0:
raise ct.WinError(ct.get_last_error())
return result
user32 = ct.WinDLL('user32', use_last_error=True)
SendInput = user32.SendInput
SendInput.argtypes = w.UINT, ct.POINTER(INPUT), ct.c_int
SendInput.restype = w.UINT
SendInput.errcheck = zerocheck
def send_scancode(code):
i = INPUT()
i.type = INPUT_KEYBOARD
i.ki = KEYBDINPUT(0, code, KEYEVENTF_SCANCODE, 0, 0)
SendInput(1, ct.byref(i), ct.sizeof(INPUT))
i.ki.dwFlags |= KEYEVENTF_KEYUP
SendInput(1, ct.byref(i), ct.sizeof(INPUT))
def send_unicode(s):
i = INPUT()
i.type = INPUT_KEYBOARD
# Handles non-BMP code points by sending UTF-16 code units.
for c in array.array('H', s.encode('utf-16le')):
i.ki = KEYBDINPUT(0, c, KEYEVENTF_UNICODE, 0, 0)
SendInput(1, ct.byref(i), ct.sizeof(INPUT))
i.ki.dwFlags |= KEYEVENTF_KEYUP
SendInput(1, ct.byref(i), ct.sizeof(INPUT))
# Allow time to focus on another app
# such as Notepad or Notepad++
time.sleep(3)
send_scancode(SPACE)
# Last character is 7 code points:
# U+1F468 MAN
# U+200D ZERO WIDTH JOINER
# U+1F469 WOMAN
# U+200D ZERO WIDTH JOINER
# U+1F467 GIRL
# U+200D ZERO WIDTH JOINER
# U+1F466 BOY
send_unicode('The quick brown fox jumped over the lazy dog๐จโ๐ฉโ๐งโ๐ฆ')
Output to Notepad.exe:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With