I'm semi-new to ctypes and I'm having trouble with how to use the GetRawInputData function. I'm not sure how to fill in the 3rd argument. Here's how the code looks right now
def get_raw_input(handle):
dw_size = c_uint()
GetRawInputData(handle, RID_INPUT, None, byref(dw_size), sizeof(RawInputHeader))
lpb = LPBYTE(dw_size)
print(lpb.contents)
GetRawInputData(handle, RID_INPUT, lpb, byref(dw_size), sizeof(RawInputHeader))
print(lpb.contents)
return cast(lpb, POINTER(RawInput))
I've defined the module like so:
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = INT, UINT, LPVOID, PUINT, UINT
GetRawInputData.restype = UINT
GetRawInputData.errcheck = errcheck
When get_raw_input
is run, the output is one of either of these:
c_byte(48)
c_byte(0)
c_byte(40)
c_byte(1)
There's some example C code here, but I'm not sure how that should look in Python.
Here's the full running code (sorry if it's a bit messy):
window.py (file that runs)
from ctypes import *
from ctypes.wintypes import *
from structures import *
from constants import *
from devices import get_raw_input, register_devices
import random
import sys
def errcheck(result,func,args):
if result is None or result == 0:
raise WinError(get_last_error())
return result
user32 = WinDLL('user32', use_last_error=True)
k32 = WinDLL('kernel32', use_last_error=True)
gdi32 = WinDLL('gdi32',use_last_error=True)
RegisterClass = user32.RegisterClassW
RegisterClass.argtypes = POINTER(WndClass),
RegisterClass.restype = ATOM
RegisterClass.errcheck = errcheck
CreateWindowEx = user32.CreateWindowExW
CreateWindowEx.argtypes = DWORD, LPCWSTR, LPCWSTR, DWORD, INT, INT, INT, INT, HWND, HMENU, HINSTANCE, LPVOID
CreateWindowEx.restype = HWND
CreateWindowEx.errcheck = errcheck
GetModuleHandle = k32.GetModuleHandleW
GetModuleHandle.argtypes = LPCWSTR,
GetModuleHandle.restype = HMODULE
GetModuleHandle.errcheck = errcheck
DefWindowProc = user32.DefWindowProcW
DefWindowProc.argtypes = HWND, UINT, WPARAM, LPARAM
DefWindowProc.restype = LRESULT
ShowWindow = user32.ShowWindow
ShowWindow.argtypes = HWND, INT
ShowWindow.restype = BOOL
UpdateWindow = user32.UpdateWindow
UpdateWindow.argtypes = HWND,
UpdateWindow.restype = BOOL
UpdateWindow.errcheck = errcheck
BeginPaint = user32.BeginPaint
BeginPaint.argtypes = HWND, POINTER(PaintStruct)
BeginPaint.restype = HDC
BeginPaint.errcheck = errcheck
FillRect = user32.FillRect
FillRect.argtypes = HDC, POINTER(RECT), HBRUSH
FillRect.restype = INT
FillRect.errcheck = errcheck
EndPaint = user32.EndPaint
EndPaint.argtypes = HWND, POINTER(PaintStruct)
EndPaint.restype = BOOL
EndPaint.errcheck = errcheck
PostQuitMessage = user32.PostQuitMessage
PostQuitMessage.argtypes = INT,
PostQuitMessage.restype = None
TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = POINTER(MSG),
TranslateMessage.restype = BOOL
DispatchMessage = user32.DispatchMessageW
DispatchMessage.argtypes = POINTER(MSG),
DispatchMessage.restype = LRESULT
GetClientRect = user32.GetClientRect
GetClientRect.argtypes = HWND, POINTER(RECT)
GetClientRect.restype = BOOL
GetClientRect.errcheck = errcheck
GetMessage = user32.GetMessageW
GetMessage.argtypes = POINTER(MSG), HWND, UINT, UINT
GetMessage.restype = BOOL
DrawText = user32.DrawTextW
DrawText.argtypes = HDC, LPCWSTR, INT, POINTER(RECT), UINT
DrawText.restype = LRESULT
LoadIcon = user32.LoadIconW
LoadIcon.argtypes = HINSTANCE, LPCWSTR
LoadIcon.restype = HICON
LoadIcon.errcheck = errcheck
LoadCursor = user32.LoadCursorW
LoadCursor.argtypes = HINSTANCE, LPCWSTR
LoadCursor.restype = HCURSOR
LoadCursor.errcheck = errcheck
GetStockObject = gdi32.GetStockObject
GetStockObject.argtypes = INT,
GetStockObject.restype = HGDIOBJ
CreateSolidBrush = gdi32.CreateSolidBrush
CreateSolidBrush.argtypes = COLORREF,
CreateSolidBrush.restype = HBRUSH
TextOut = gdi32.TextOutW
TextOut.argtypes = HDC, INT, INT, LPCWSTR, INT
TextOut.restype = INT
SetBkMode = gdi32.SetBkMode
SetBkMode.argtypes = HDC, INT
SetBkMode.restype = INT
SetTextColor = gdi32.SetTextColor
SetTextColor.argtypes = HDC, COLORREF
SetTextColor.restype = COLORREF
def window_callback(handle, message, w_param, l_param):
ps = PaintStruct()
rect = RECT()
if message == WM_PAINT:
hdc = BeginPaint(handle, byref(ps))
GetClientRect(handle, byref(rect))
SetBkMode(hdc, -1)
SetTextColor(hdc, 0xFFFFFF)
FillRect(hdc, byref(ps.rcPaint), CreateSolidBrush(0x0))
DrawText(hdc, 'Testing oawguna', -1, byref(rect), DT_SINGLELINE|DT_CENTER|DT_VCENTER)
EndPaint(handle, byref(ps))
return 0
elif message == WM_INPUT:
raw_input = get_raw_input(l_param)
elif message == WM_DESTROY:
PostQuitMessage(0)
return 0
return DefWindowProc(handle, message, w_param, l_param)
def run():
instance = GetModuleHandle(None)
class_name = 'TestWindow'
wnd = WndClass()
wnd.style = CS_HREDRAW | CS_VREDRAW
wnd.lpfnWndProc = WNDPROC(window_callback)
wnd.hInstace = instance
wnd.hCursor = LoadCursor(None, IDC_ARROW)
wnd.lpszClassName = class_name
RegisterClass(byref(wnd))
handle = CreateWindowEx(
0, class_name, 'Python Window', WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
None, None, instance, None
)
ShowWindow(handle, SW_NORMAL)
UpdateWindow(handle)
register_devices(handle)
msg = MSG()
while GetMessage(byref(msg), None, 0, 0) != 0:
TranslateMessage(byref(msg))
DispatchMessage(byref(msg))
return msg.wParam
if __name__ == "__main__":
sys.exit(run())
devices.py:
from ctypes import *
from ctypes.wintypes import *
from structures import *
from constants import *
def errcheck(result,func,args):
if result is None or result == -1:
raise WinError(get_last_error())
return result
user32 = WinDLL('user32', use_last_error=True)
RegisterRawInputDevices = user32.RegisterRawInputDevices
RegisterRawInputDevices.argtypes = (RawInputDevice * 7), UINT, UINT
RegisterRawInputDevices.restype = BOOL
RegisterRawInputDevices.errcheck = errcheck
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = INT, UINT, LPVOID, PUINT, UINT
GetRawInputData.restype = UINT
GetRawInputData.errcheck = errcheck
def register_devices(hwnd_target=None):
page = 0x01
devices = (RawInputDevice * 7)(
RawInputDevice(page, 0x01, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x02, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x04, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x05, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x06, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x07, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x08, DW_FLAGS, hwnd_target),
)
RegisterRawInputDevices(devices, len(devices), sizeof(devices[0]))
def get_raw_input(handle):
dw_size = c_uint()
GetRawInputData(handle, RID_INPUT, None, byref(dw_size), sizeof(RawInputHeader))
lpb = LPBYTE(dw_size)
print(lpb.contents)
GetRawInputData(handle, RID_INPUT, lpb, byref(dw_size), sizeof(RawInputHeader))
print(lpb.contents)
return cast(lpb, POINTER(RawInput))
constants.py:
from ctypes.wintypes import *
from ctypes import c_int64, WINFUNCTYPE, c_void_p
LRESULT = c_int64
HCURSOR = c_void_p
USAGE_PAGE = 0x01
USAGE = 0x06
DW_FLAGS = 0
DW_TYPE = 2
WM_INPUT = 0x00FF
WM_PAINT = 0x000F
WM_DESTROY = 0x0002
WM_QUI = 0x0012
RIDI_DEVICENAME = 0x20000007
RIDI_DEVICEINFO = 0x2000000b
ERROR_INSUFFICIENT_BUFFER = 0x7A
WS_EX_CLIENTEDGE = 0x00000200
WS_BORDER = 0x00800000
CS_BYTEALIGNCLIENT = 0x1000
CS_HREDRAW = 2
CS_VREDRAW = 1
SW_NORMAL = 1
CW_USEDEFAULT = -2147483648
WS_OVERLAPPED = 0x00000000
WS_CAPTION = 0x00C00000
WS_SYSMENU = 0x00080000
WS_THICKFRAME = 0x00040000
WS_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
RID_INPUT = 0x10000003
PM_REMOVE = 0x0001
DT_SINGLELINE = 32
DT_CENTER = 1
DT_VCENTER = 4
IDI_APPLICATION = LPCWSTR(32512)
IDC_ARROW = LPCWSTR(32512)
WHITE_BRUSH = 0
WNDPROC = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
structures.py:
from ctypes import Structure, windll, WINFUNCTYPE, c_int64, Union, c_byte, c_void_p
from ctypes.wintypes import *
from constants import *
HCURSOR = c_void_p
class RawInputDevice(Structure):
_fields_ = [
("usUsagePage", USHORT),
("usUsage", USHORT),
("dwFlags", DWORD),
("hwndTarget", HWND),
]
class RawInputDeviceList(Structure):
_fields_ = [
("hDevice", HANDLE),
("dwType", DWORD),
]
class RIDDeviceInfoHID(Structure):
_fields_ = [
("dwVendorId", DWORD),
("dwProductId", DWORD),
("dwVersionNumber", DWORD),
("usUsagePage", USHORT),
("usUsage", USHORT),
]
class RIDDeviceInfo(Structure):
_fields_ = [
("cbSize", DWORD),
("dwType", DWORD),
("hid", RIDDeviceInfoHID),
]
class WndClass(Structure):
_fields_ = [
("style", UINT),
("lpfnWndProc", WNDPROC),
("cbClsExtra", INT),
("cbWndExtra", INT),
("hInstance", HINSTANCE),
("hIcon", HICON),
("hCursor", HCURSOR),
("hbrBackground", HBRUSH),
("lpszMenuName", LPCWSTR),
("lpszClassName", LPCWSTR),
]
class PaintStruct(Structure):
_fields_ = [
("hdc", HDC),
("fErase", BOOL),
("rcPaint", RECT),
("fRestore", BOOL),
("fIncUpdate", BOOL),
("rgbReserved", BYTE * 32),
]
class RawInputHeader(Structure):
_fields_ = [
("dwType", DWORD),
("dwSize", DWORD),
("hDevice", HANDLE),
("wParam", WPARAM),
]
class RawHID(Structure):
_fields_ = [
("dwSizeHid", DWORD),
("dwCount", DWORD),
("bRawData", BYTE),
]
class MouseStruct(Structure):
_fields_ = [
("usButtonFlags", USHORT),
("usButtonData", USHORT)
]
class MouseUnion(Union):
_fields_ = [
("ulButtons", ULONG),
("data", MouseStruct)
]
class RawMouse(Structure):
_fields_ = [
("usFlags", USHORT),
("data", MouseUnion),
("ulRawButtons", ULONG),
("lLastX", LONG),
("lLastY", LONG),
("ulExtraInformation", ULONG),
]
class RawKeyboard(Structure):
_fields_ = [
("MakeCode", USHORT),
("Flags", USHORT),
("Reserved", USHORT),
("VKey", USHORT),
("Message", UINT),
("ExtraInformation", ULONG),
]
class RawUnion(Union):
_fields_ = [
("mouse", RawMouse),
("keyboard", RawKeyboard),
("hid", RawHID)
]
class RawInput(Structure):
_fields_ = [
("header", RawInputHeader),
("data", RawUnion),
]
CodePudding user response:
Listing [Python.Docs]: ctypes - A foreign function library for Python.
This (a fix) was already covered as part of [SO]: Having trouble using winapi to read input from a device (@CristiFati's answer).
The problem is LPBYTE (POINTER(c_byte)) initialization:
Without parameters, wraps a NULL pointer
With parameter (an integer value) it will store that value in the pointed area (and not use it as size).
More, that area will be 064bit large (discovered experimentally) meaning that trying to index outside it triggers Undefined Behavior, even if the program doesn't crash (that is because the area right next isn't claimed by other variables)
To allocate a memory area of a certain size, use create_string_buffer (or an array - but you'll have to cast when passing it to functions expecting pointers).
>>> import ctypes as ct >>> import ctypes.wintypes as wt >>> >>> >>> pb0 = wt.LPBYTE() >>> bool(pb0) False >>> pb0.contents Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: NULL pointer access >>> >>> ll = ct.c_longlong(0x010203040506070809) # Will be truncated (head part - due to little endianness) >>> pb1 = wt.LPBYTE(ll) >>> bool(pb1) True >>> pb1.contents c_byte(9) >>> [pb1[e] for e in range(0, 10)] # @TODO - cfati: will genarate UB !!! [9, 8, 7, 6, 5, 4, 3, 2, 0, 0] >>> >>> buf = ct.create_string_buffer(12) >>> len(buf) 12 >>> buf[0] b'\x00' >>> buf[12] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: invalid index