I have a code use SetupDiGetDeviceRegistryPropertyW to enumerate USB vendor id and product id , I read from buffer as <ctypes.LP_c_wchar object at 0x000001A3361F4C40>,how can i parse ?
Buffer
result = ct.cast(PropertyBuffer,ct.POINTER(w.WCHAR))
code
import ctypes as ct
from ctypes import wintypes as w
import uuid
SetupAPI = ct.WinDLL('SetupAPI')
ULONG_PTR = w.WPARAM
class HDEVINFO(w.HANDLE):
pass
class GUID(ct.Structure):
_fields_ = (('Data1', ct.c_ulong),
('Data2', ct.c_ushort),
('Data3', ct.c_ushort),
('Data4', ct.c_ubyte * 8))
def __repr__(self):
return f"GUID('{self}')"
def __str__(self):
return (f'{{{self.Data1:08x}-{self.Data2:04x}-{self.Data3:04x}-'
f'{bytes(self.Data4[:2]).hex()}-{bytes(self.Data4[2:]).hex()}}}')
def __init__(self,guid=None):
if guid is not None:
data = uuid.UUID(guid)
self.Data1 = data.time_low
self.Data2 = data.time_mid
self.Data3 = data.time_hi_version
self.Data4[0] = data.clock_seq_hi_variant
self.Data4[1] = data.clock_seq_low
self.Data4[2:] = data.node.to_bytes(6,'big')
PGUID = ct.POINTER(GUID)
GUID_DEVINTERFACE_USB_DEVICE = GUID('{A5DCBF10-6530-11D2-901F-00C04FB951ED}')
class SP_DEVINFO_DATA(ct.Structure):
_fields_ = (('cbSize', w.DWORD),
('ClassGuid', GUID),
('DevInst', w.DWORD),
('Reserved', ULONG_PTR))
def __repr__(self):
return f'SP_DEVINFO_DATA(ClassGuid={self.ClassGuid}, DevInst={self.DevInst})'
def __init__(self):
self.cbSize = ct.sizeof(SP_DEVINFO_DATA)
PSP_DEVINFO_DATA = ct.POINTER(SP_DEVINFO_DATA)
SetupAPI.SetupDiGetClassDevsW.argtypes = PGUID, w.PWCHAR, w.HWND, w.DWORD
SetupAPI.SetupDiGetClassDevsW.restype = HDEVINFO
SetupAPI.SetupDiEnumDeviceInfo.argtypes = HDEVINFO, w.DWORD, PSP_DEVINFO_DATA
SetupAPI.SetupDiEnumDeviceInfo.restype = w.BOOL
SetupAPI.SetupDiGetDeviceRegistryPropertyA.argtypes = HDEVINFO, PSP_DEVINFO_DATA,
w.DWORD, w.PDWORD, w.PBYTE, w.DWORD, w.PDWORD
SetupAPI.SetupDiGetDeviceRegistryPropertyA.restype = w.BOOL
SetupAPI.SetupDiDestroyDeviceInfoList.argtypes = HDEVINFO,
SetupAPI.SetupDiDestroyDeviceInfoList.restype = w.BOOL
DIGCF_DEFAULT = 0x00000001
DIGCF_PRESENT = 0x00000002
DIGCF_ALLCLASSES = 0x00000004
DIGCF_PROFILE = 0x00000008
DIGCF_DEVICEINTERFACE = 0x00000010
ClassGuid = GUID_DEVINTERFACE_USB_DEVICE
Enumerator = None
hwndParent = None
Flags = DIGCF_DEVICEINTERFACE | DIGCF_PRESENT
devinfo = SP_DEVINFO_DATA()
hDevInfo = SetupAPI.SetupDiGetClassDevsW(ClassGuid, Enumerator, hwndParent, Flags)
DeviceInfoSet = hDevInfo
DeviceInfoData = ct.byref(devinfo)
SPDRP_HARDWAREID = 0x00000001
PropertyRegDataType = None
PropertyBufferSize = 0
RequiredSize = w.DWORD()
try:
MemberIndex = 0
while SetupAPI.SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, ct.byref(devinfo)):
print(devinfo)
SetupAPI.SetupDiGetDeviceRegistryPropertyW(
hDevInfo,
ct.byref(devinfo),
SPDRP_HARDWAREID,
PropertyRegDataType,
None,
PropertyBufferSize ,
ct.byref(RequiredSize))
PropertyBuffer = (w.BYTE * RequiredSize.value)()
if not SetupAPI.SetupDiGetDeviceRegistryPropertyW(
hDevInfo,
ct.byref(devinfo),
SPDRP_HARDWAREID,
PropertyRegDataType,
PropertyBuffer,
ct.sizeof(PropertyBuffer),
None):
break
result = ct.cast(PropertyBuffer,ct.POINTER(w.WCHAR))
print(result)
MemberIndex += 1
finally:
SetupAPI.SetupDiDestroyDeviceInfoList(hDevInfo)
Make sure you are initializing and calling the "W" version of the SetupDiGetDeviceRegistryProperty API:
SetupAPI.SetupDiGetDeviceRegistryPropertyW.argtypes = HDEVINFO, PSP_DEVINFO_DATA, w.DWORD, w.PDWORD, w.PBYTE, w.DWORD, w.PDWORD
SetupAPI.SetupDiGetDeviceRegistryPropertyW.restype = w.BOOL
The registry value being read is a MULTI_SZ, which is multiple zero-terminated strings, the last of which is double-terminated. The following function casts the return buffer to a C wchar_t*, but then uses string slicing to extracts "length" characters starting at that pointer address. The length is determined by dividing the returned buffer size by the size of a WCHAR. Then the trailing nulls are stripped from the data and the individual strings are split by the other nulls:
def get_multi_sz(buffer, length):
result = ct.cast(buffer, ct.POINTER(w.WCHAR))
strings = result[:RequiredSize.value // ct.sizeof(w.WCHAR)]
return strings.rstrip('\x00').split('\x00')
Finally, in your SetupDiEnumDeviceInfo while loop, print the list of strings returned by each device:
MemberIndex = 0
while SetupAPI.SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, ct.byref(devinfo)):
print(devinfo)
SetupAPI.SetupDiGetDeviceRegistryPropertyW(
hDevInfo, ct.byref(devinfo), SPDRP_HARDWAREID, PropertyRegDataType,
None, PropertyBufferSize, ct.byref(RequiredSize))
PropertyBuffer = (w.BYTE * RequiredSize.value)()
if not SetupAPI.SetupDiGetDeviceRegistryPropertyW(
hDevInfo, ct.byref(devinfo), SPDRP_HARDWAREID, PropertyRegDataType,
PropertyBuffer, ct.sizeof(PropertyBuffer), None):
break
print(get_multi_sz(PropertyBuffer, RequiredSize.value))
MemberIndex += 1
Output with these changes (on my system, anyway):
SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=1)
['USB\\VID_046D&PID_C52B&REV_1201', 'USB\\VID_046D&PID_C52B']
SP_DEVINFO_DATA(ClassGuid={745a17a0-74d3-11d0-b6fe-00a0c90f57da}, DevInst=2)
['USB\\VID_046D&PID_C01B&REV_1800', 'USB\\VID_046D&PID_C01B']
SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=3)
['USB\\VID_03F0&PID_0122&REV_0100', 'USB\\VID_03F0&PID_0122']
SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=4)
['USB\\VID_046D&PID_0A87&REV_0112', 'USB\\VID_046D&PID_0A87']
Using a tool like UsbTreeView, you can verify the Hardware ID strings match. Here's an excerpt from that tool that matches the 3rd device's hardware IDs above:
======================== USB Device ========================
+++++++++++++++++ Device Information ++++++++++++++++++
Device Description : USB Composite Device
Device Path : \\?\USB#VID_03F0&PID_0122#5&35eda8e7&0&1#{a5dcbf10-6530-11d2-901f-00c04fb951ed} (GUID_DEVINTERFACE_USB_DEVICE)
Kernel Name : \Device\USBPDO-10
Device ID : USB\VID_03F0&PID_0122\5&35EDA8E7&0&1
Hardware IDs : USB\VID_03F0&PID_0122&REV_0100 USB\VID_03F0&PID_0122
Listing:
[Python.Docs]: ctypes - A foreign function library for Python
[MS.Docs]: SetupDiGetDeviceRegistryPropertyW function (setupapi.h) for info on how to parse the results. For example, according to [MS.Docs]: Registry Value Types (emphasis is mine):
REG_MULTI_SZ - A sequence of null-terminated strings, terminated by an empty string (\0). The following is an example: String1\0String2\0String3\0LastString\0\0 The first \0 terminates the first string, the second to the last \0 terminates the last string, and the final \0 terminates the sequence. Note that the final terminator must be factored into the length of the string.
Your code had several flaws (out of which matter most):
Continuation on a separate line of SetupAPI.SetupDiGetDeviceRegistryPropertyA.argtypes (without a \) - your program only works because the following bullet is masking this
Definig them for SetupDiGetDeviceRegistryPropertyA but using SetupDiGetDeviceRegistryPropertyW - which yields Undefined Behavior. Check [SO]: C function called from Python via ctypes returns incorrect value (#CristiFati's answer) for more details
Converting the array to a pointer doesn't make any sense, as it loses size information
I modified your script to a working version:
Extended parse_data to accommodate new data types (REG_DWORD, REG_SZ, REG_MULTI_SZ) - as originally only REG_MULTI_SZ data (corresponding to SPDRP_HARDWAREID property) was mentioned / required
Improved error handling
Extended functionality
Other minor things
code00.py:
#!/usr/bin/env python
import ctypes as ct
import sys
import uuid
import winreg as wr
from ctypes import wintypes as wt
ULONG_PTR = wt.WPARAM
HDEVINFO = wt.HANDLE
class GUID(ct.Structure):
_fields_ = (
("Data1", ct.c_ulong),
("Data2", ct.c_ushort),
("Data3", ct.c_ushort),
("Data4", ct.c_ubyte * 8),
)
def __repr__(self):
return f"GUID('{self}')"
def __str__(self):
return (f"{{{self.Data1:08x}-{self.Data2:04x}-{self.Data3:04x}-"
f"{bytes(self.Data4[:2]).hex()}-{bytes(self.Data4[2:]).hex()}}}")
def __init__(self,guid=None):
if guid is not None:
data = uuid.UUID(guid)
self.Data1 = data.time_low
self.Data2 = data.time_mid
self.Data3 = data.time_hi_version
self.Data4[0] = data.clock_seq_hi_variant
self.Data4[1] = data.clock_seq_low
self.Data4[2:] = data.node.to_bytes(6, "big")
PGUID = ct.POINTER(GUID)
class SP_DEVINFO_DATA(ct.Structure):
_fields_ = (
("cbSize", wt.DWORD),
("ClassGuid", GUID),
("DevInst", wt.DWORD),
("Reserved", ULONG_PTR),
)
def __repr__(self):
return f"SP_DEVINFO_DATA(ClassGuid={self.ClassGuid}, DevInst={self.DevInst})"
def __init__(self):
self.cbSize = ct.sizeof(self.__class__)
PSP_DEVINFO_DATA = ct.POINTER(SP_DEVINFO_DATA)
GUID_DEVINTERFACE_USB_DEVICE = GUID("{A5DCBF10-6530-11D2-901F-00C04FB951ED}")
kernel32 = ct.WinDLL("Kernel32")
GetLastError = kernel32.GetLastError
GetLastError.argtypes = ()
GetLastError.restype = wt.DWORD
setupapi = ct.WinDLL("Setupapi")
#is_wide = True # Determine whether using WIDE or ANSI functions
SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW #if is_wide else setupapi.SetupDiGetClassDevsA
SetupDiGetClassDevs.argtypes = (PGUID, wt.PWCHAR, wt.HWND, wt.DWORD)
SetupDiGetClassDevs.restype = HDEVINFO
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
SetupDiEnumDeviceInfo.argtypes = (HDEVINFO, wt.DWORD, PSP_DEVINFO_DATA)
SetupDiEnumDeviceInfo.restype = wt.BOOL
SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW #if is_wide else setupapi.SetupDiGetDeviceRegistryPropertyA
SetupDiGetDeviceRegistryProperty.argtypes = (HDEVINFO, PSP_DEVINFO_DATA, wt.DWORD, wt.PDWORD, wt.PBYTE, wt.DWORD, wt.PDWORD)
SetupDiGetDeviceRegistryProperty.restype = wt.BOOL
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
SetupDiDestroyDeviceInfoList.argtypes = (HDEVINFO,)
SetupDiDestroyDeviceInfoList.restype = wt.BOOL
DIGCF_DEFAULT = 0x00000001
DIGCF_PRESENT = 0x00000002
DIGCF_ALLCLASSES = 0x00000004
DIGCF_PROFILE = 0x00000008
DIGCF_DEVICEINTERFACE = 0x00000010
def parse_data(arr, reg_data_type):
if reg_data_type is wr.REG_DWORD:
return wt.DWORD.from_buffer(arr).value
elif reg_data_type in (wr.REG_SZ, wr.REG_MULTI_SZ):
WArrType = wt.WCHAR * (arr._length_ // ct.sizeof(wt.WCHAR)) # Convert from char array to wchar_t array (halving size)
warr = WArrType.from_buffer(arr) # #TODO - cfati: You should probably use MultiByteToWideChar or mbstowcs here.
ret = str(warr[:len(warr)]).rstrip("\x00")
if reg_data_type is wr.REG_MULTI_SZ:
return ret.split("\x00")
elif reg_data_type is wr.REG_SZ:
return ret
else: # More types could be handled here
return arr
def main(*argv):
ClassGuid = GUID_DEVINTERFACE_USB_DEVICE
Enumerator = None
hwndParent = None
Flags = DIGCF_DEVICEINTERFACE | DIGCF_PRESENT
dev_info = SP_DEVINFO_DATA()
hDevInfo = SetupDiGetClassDevs(ClassGuid, Enumerator, hwndParent, Flags)
DeviceInfoSet = hDevInfo
property_reg_data_type = wt.DWORD(0)
required_size = wt.DWORD(0)
SPDRP_HARDWAREID = 0x00000001
SPDRP_CLASS = 0x00000007
SPDRP_FRIENDLYNAME = 0x0000000C
SPDRP_CAPABILITIES = 0x0000000F
SPDRP_BUSNUMBER = 0x00000015
SPDRP_DEVTYPE = 0x00000019
SPDRP_EXCLUSIVE = 0x0000001A
SPDRP_CHARACTERISTICS = 0x0000001B
SPDRP_ADDRESS = 0x0000001C
SPDRP_INSTALL_STATE = 0x00000022
props = (SPDRP_HARDWAREID, SPDRP_CLASS, SPDRP_FRIENDLYNAME, SPDRP_CAPABILITIES)
ERROR_INVALID_DATA = 0x0D
ERROR_INSUFFICIENT_BUFFER = 0x7A
try:
MemberIndex = 0
while SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, ct.byref(dev_info)):
print(dev_info)
keep_running = True
for prop in props:
print(f" Prop: {prop}")
if not SetupDiGetDeviceRegistryProperty(
hDevInfo, ct.byref(dev_info), prop,
None, None, 0, ct.byref(required_size)):
gle = GetLastError()
if gle == ERROR_INVALID_DATA:
print(" Unsupported")
continue
elif gle != ERROR_INSUFFICIENT_BUFFER:
print(" Fail0:", gle)
keep_running = False
break
#print(" kkt", prop, required_size.value)
PropertyBuffer = wt.BYTE * required_size.value
property_buffer = PropertyBuffer()
if not SetupDiGetDeviceRegistryProperty(
hDevInfo, ct.byref(dev_info), prop,
ct.byref(property_reg_data_type),
property_buffer, ct.sizeof(PropertyBuffer), None):
gle = GetLastError()
print(" Fail1:", gle)
if gle != ERROR_INVALID_DATA:
keep_running = False
break
print(f" Data type: {property_reg_data_type.value} \
\n Buf: {property_buffer} \
\n Data: {parse_data(property_buffer, property_reg_data_type.value)}")
if not keep_running:
break
MemberIndex += 1
finally:
SetupDiDestroyDeviceInfoList(hDevInfo)
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
Output:
[cfati#CFATI-5510-0:e:\Work\Dev\StackOverflow\q072847468]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" ./code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32
SP_DEVINFO_DATA(ClassGuid={4d36e972-e325-11ce-bfc1-08002be10318}, DevInst=1)
Prop: 1
Data type: 7
Buf: <__main__.c_byte_Array_108 object at 0x000002287E6F4F40>
Data: ['USB\\VID_0BDA&PID_8153&REV_3100', 'USB\\VID_0BDA&PID_8153']
Prop: 7
Data type: 1
Buf: <__main__.c_byte_Array_8 object at 0x000002287ECF60C0>
Data: Net
Prop: 12
Data type: 1
Buf: <__main__.c_byte_Array_74 object at 0x000002287E6F4F40>
Data: Realtek USB GbE Family Controller #2
Prop: 15
Data type: 4
Buf: <__main__.c_byte_Array_4 object at 0x000002287ECF6040>
Data: 148
SP_DEVINFO_DATA(ClassGuid={745a17a0-74d3-11d0-b6fe-00a0c90f57da}, DevInst=2)
Prop: 1
Data type: 7
Buf: <__main__.c_byte_Array_108 object at 0x000002287E6F4F40>
Data: ['USB\\VID_045E&PID_00CB&REV_0100', 'USB\\VID_045E&PID_00CB']
Prop: 7
Data type: 1
Buf: <__main__.c_byte_Array_18 object at 0x000002287ECF8040>
Data: HIDClass
Prop: 12
Unsupported
Prop: 15
Data type: 4
Buf: <__main__.c_byte_Array_4 object at 0x000002287E6F4F40>
Data: 132
SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=3)
Prop: 1
Data type: 7
Buf: <__main__.c_byte_Array_108 object at 0x000002287ECF80C0>
Data: ['USB\\VID_045E&PID_07F8&REV_0300', 'USB\\VID_045E&PID_07F8']
Prop: 7
Data type: 1
Buf: <__main__.c_byte_Array_8 object at 0x000002287E6F4F40>
Data: USB
Prop: 12
Unsupported
Prop: 15
Data type: 4
Buf: <__main__.c_byte_Array_4 object at 0x000002287ECF8040>
Data: 132
SP_DEVINFO_DATA(ClassGuid={e0cbf06c-cd8b-4647-bb8a-263b43f0f974}, DevInst=4)
Prop: 1
Data type: 7
Buf: <__main__.c_byte_Array_108 object at 0x000002287E6F4F40>
Data: ['USB\\VID_8087&PID_0A2B&REV_0001', 'USB\\VID_8087&PID_0A2B']
Prop: 7
Data type: 1
Buf: <__main__.c_byte_Array_20 object at 0x000002287ECF80C0>
Data: Bluetooth
Prop: 12
Unsupported
Prop: 15
Data type: 4
Buf: <__main__.c_byte_Array_4 object at 0x000002287E6F4F40>
Data: 128
SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=5)
Prop: 1
Data type: 7
Buf: <__main__.c_byte_Array_108 object at 0x000002287ECF8040>
Data: ['USB\\VID_0C45&PID_6713&REV_5605', 'USB\\VID_0C45&PID_6713']
Prop: 7
Data type: 1
Buf: <__main__.c_byte_Array_8 object at 0x000002287E6F4F40>
Data: USB
Prop: 12
Unsupported
Prop: 15
Data type: 4
Buf: <__main__.c_byte_Array_4 object at 0x000002287ECF80C0>
Data: 128
Done.
Related
When I try to run rcReceiver_IBUS.py, I am getting this error:
module 'uctypes' has no attribute 'INT32'
timer.py line 17
Your program tries to access attribute INT32 of an object of type 'uctypes', but this type doesn't have such attribute.
.
rcReceiver_IBUS.py
from machine import UART
import time
buffer=bytearray(31)
uart = UART(1, 115200) # uart1 tx-pin 4, rx-pin 5
while True:
c=uart.read(1)
if c[0] == 0x20:
uart.readinto(buffer)
checksum = 0xffff - 0x20
for i in range(29): checksum -= buffer[i]
if checksum == (buffer[30] << 8) | buffer[29]:
# skip buffer[0] = 0x40
ch1 = buffer[2]*255+buffer[1]
ch2 = buffer[4]*255+buffer[3]
ch3 = buffer[6]*255+buffer[5]
ch4 = buffer[8]*255+buffer[7]
ch5 = buffer[10]*255+buffer[9]
ch6 = buffer[12]*255+buffer[11]
print('ch 1-',ch1,' 2-',ch2,' 3-',ch3,' 4-',ch4,' 5-',ch5,' 6-',ch6)
.
timer.py
import ffilib
import uctypes
import array
import uos
import os
import utime
from signal import *
libc = ffilib.libc()
librt = ffilib.open("librt")
CLOCK_REALTIME = 0
CLOCK_MONOTONIC = 1
SIGEV_SIGNAL = 0
sigval_t = {
"sival_int": uctypes.INT32 | 0,
"sival_ptr": (uctypes.PTR | 0, uctypes.UINT8),
}
sigevent_t = {
"sigev_value": (0, sigval_t),
"sigev_signo": uctypes.INT32 | 8,
"sigev_notify": uctypes.INT32 | 12,
}
timespec_t = {
"tv_sec": uctypes.INT32 | 0,
"tv_nsec": uctypes.INT64 | 8,
}
itimerspec_t = {
"it_interval": (0, timespec_t),
"it_value": (16, timespec_t),
}
__libc_current_sigrtmin = libc.func("i", "__libc_current_sigrtmin", "")
SIGRTMIN = __libc_current_sigrtmin()
timer_create_ = librt.func("i", "timer_create", "ipp")
timer_settime_ = librt.func("i", "timer_settime", "PiPp")
def new(sdesc):
buf = bytearray(uctypes.sizeof(sdesc))
s = uctypes.struct(uctypes.addressof(buf), sdesc, uctypes.NATIVE)
return s
def timer_create(sig_id):
sev = new(sigevent_t)
#print(sev)
sev.sigev_notify = SIGEV_SIGNAL
sev.sigev_signo = SIGRTMIN + sig_id
timerid = array.array('P', [0])
r = timer_create_(CLOCK_MONOTONIC, sev, timerid)
os.check_error(r)
#print("timerid", hex(timerid[0]))
return timerid[0]
def timer_settime(tid, hz):
period = 1000000000 // hz
new_val = new(itimerspec_t)
new_val.it_value.tv_nsec = period
new_val.it_interval.tv_nsec = period
#print("new_val:", bytes(new_val))
old_val = new(itimerspec_t)
#print(new_val, old_val)
r = timer_settime_(tid, 0, new_val, old_val)
os.check_error(r)
#print("old_val:", bytes(old_val))
#print("timer_settime", r)
class Timer:
def __init__(self, id, freq):
self.id = id
self.tid = timer_create(id)
self.freq = freq
def callback(self, cb):
self.cb = cb
timer_settime(self.tid, self.freq)
org_sig = signal(SIGRTMIN + self.id, self.handler)
#print("Sig %d: %s" % (SIGRTMIN + self.id, org_sig))
def handler(self, signum):
#print('Signal handler called with signal', signum)
self.cb(self)
But when I open uctypes.py, INT32 is in the file. It seems there is no problem in file. What am I missing?
.
uctypes.py
ARRAY = -1073741824
BFINT16 = -671088640
BFINT32 = -402653184
BFINT8 = -939524096
BFUINT16 = -805306368
BFUINT32 = -536870912
BFUINT8 = -1073741824
BF_LEN = 22
BF_POS = 17
BIG_ENDIAN = 1
FLOAT32 = -268435456
FLOAT64 = -134217728
INT16 = 402653184
INT32 = 671088640
INT64 = 939524096
INT8 = 134217728
LITTLE_ENDIAN = 0
NATIVE = 2
PTR = 536870912
UINT16 = 268435456
UINT32 = 536870912
UINT64 = 805306368
UINT8 = 0
VOID = 0
def addressof():
pass
def bytearray_at():
pass
def bytes_at():
pass
def sizeof():
pass
class struct:
""
I have a USB controller that I'm trying to get inputs from, a Microsoft® SideWinder® Plug & Play Game Pad. I'm having difficulties trying to figure out how to receive its inputs correctly. Unfortunately, I cannot use pygame as it requires a window to receive inputs from, but I have to generate a pyglet window (via PsychoPy) to run my program. With pygame it can connect and show the state of buttons, but it cannot receive inputs without creating a window. I tried looking for other libraries, but all I encountered was Inputs, which isn't compatible with my controller (doesn't detect the device after installing). The controller itself works as I've tested it with an online gamepad tester. PsychoPy's joystick API is currently broken and does not work, so no luck there either.
I was really hoping anyone had advice on how to receive inputs from my controller/gamepad into my program?
For Windows you can use the WINMM.dll directly.
Use the ctypes library to load the dll (see Loading shared libraries). Use ctypes.WINFUNCTYPE to create prototypes for the function:
joyGetNumDevs
joyGetDevCapsW
joyGetPosEx
import ctypes
winmmdll = ctypes.WinDLL('winmm.dll')
# [joyGetNumDevs](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetnumdevs)
"""
UINT joyGetNumDevs();
"""
joyGetNumDevs_proto = ctypes.WINFUNCTYPE(ctypes.c_uint)
joyGetNumDevs_func = joyGetNumDevs_proto(("joyGetNumDevs", winmmdll))
# [joyGetDevCaps](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetdevcaps)
"""
MMRESULT joyGetDevCaps(UINT uJoyID, LPJOYCAPS pjc, UINT cbjc);
32 bit: joyGetDevCapsA
64 bit: joyGetDevCapsW
sizeof(JOYCAPS): 728
"""
joyGetDevCaps_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint)
joyGetDevCaps_param = (1, "uJoyID", 0), (1, "pjc", None), (1, "cbjc", 0)
joyGetDevCaps_func = joyGetDevCaps_proto(("joyGetDevCapsW", winmmdll), joyGetDevCaps_param)
# [joyGetPosEx](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetposex)
"""
MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji);
sizeof(JOYINFOEX): 52
"""
joyGetPosEx_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p)
joyGetPosEx_param = (1, "uJoyID", 0), (1, "pji", None)
joyGetPosEx_func = joyGetPosEx_proto(("joyGetPosEx", winmmdll), joyGetPosEx_param)
Create the python function joyGetNumDevs, joyGetDevCaps and joyGetPosEx which delegate to the DLL. And create classes for the types JOYCAPS respectively JOYINFOEX:
# joystickapi - joyGetNumDevs
def joyGetNumDevs():
try:
num = joyGetNumDevs_func()
except:
num = 0
return num
# joystickapi - joyGetDevCaps
def joyGetDevCaps(uJoyID):
try:
buffer = (ctypes.c_ubyte * JOYCAPS.SIZE_W)()
p1 = ctypes.c_uint(uJoyID)
p2 = ctypes.cast(buffer, ctypes.c_void_p)
p3 = ctypes.c_uint(JOYCAPS.SIZE_W)
ret_val = joyGetDevCaps_func(p1, p2, p3)
ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYCAPS(buffer))
except:
ret = False, None
return ret
# joystickapi - joyGetPosEx
def joyGetPosEx(uJoyID):
try:
buffer = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4))()
buffer[0] = JOYINFOEX.SIZE
buffer[1] = JOY_RETURNALL
p1 = ctypes.c_uint(uJoyID)
p2 = ctypes.cast(buffer, ctypes.c_void_p)
ret_val = joyGetPosEx_func(p1, p2)
ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYINFOEX(buffer))
except:
ret = False, None
return ret
JOYERR_NOERROR = 0
JOY_RETURNX = 0x00000001
JOY_RETURNY = 0x00000002
JOY_RETURNZ = 0x00000004
JOY_RETURNR = 0x00000008
JOY_RETURNU = 0x00000010
JOY_RETURNV = 0x00000020
JOY_RETURNPOV = 0x00000040
JOY_RETURNBUTTONS = 0x00000080
JOY_RETURNRAWDATA = 0x00000100
JOY_RETURNPOVCTS = 0x00000200
JOY_RETURNCENTERED = 0x00000400
JOY_USEDEADZONE = 0x00000800
JOY_RETURNALL = (JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | \
JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | \
JOY_RETURNPOV | JOY_RETURNBUTTONS)
# joystickapi - JOYCAPS
class JOYCAPS:
SIZE_W = 728
OFFSET_V = 4 + 32*2
def __init__(self, buffer):
ushort_array = (ctypes.c_uint16 * 2).from_buffer(buffer)
self.wMid, self.wPid = ushort_array
wchar_array = (ctypes.c_wchar * 32).from_buffer(buffer, 4)
self.szPname = ctypes.cast(wchar_array, ctypes.c_wchar_p).value
uint_array = (ctypes.c_uint32 * 19).from_buffer(buffer, JOYCAPS.OFFSET_V)
self.wXmin, self.wXmax, self.wYmin, self.wYmax, self.wZmin, self.wZmax, \
self.wNumButtons, self.wPeriodMin, self.wPeriodMax, \
self.wRmin, self.wRmax, self.wUmin, self.wUmax, self.wVmin, self.wVmax, \
self.wCaps, self.wMaxAxes, self.wNumAxes, self.wMaxButtons = uint_array
# joystickapi - JOYINFOEX
class JOYINFOEX:
SIZE = 52
def __init__(self, buffer):
uint_array = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4)).from_buffer(buffer)
self.dwSize, self.dwFlags, \
self.dwXpos, self.dwYpos, self.dwZpos, self.dwRpos, self.dwUpos, self.dwVpos, \
self.dwButtons, self.dwButtonNumber, self.dwPOV, self.dwReserved1, self.dwReserved2 = uint_array
See the simple example to test the API:
import joystickapi
import msvcrt
import time
print("start")
num = joystickapi.joyGetNumDevs()
ret, caps, startinfo = False, None, None
for id in range(num):
ret, caps = joystickapi.joyGetDevCaps(id)
if ret:
print("gamepad detected: " + caps.szPname)
ret, startinfo = joystickapi.joyGetPosEx(id)
break
else:
print("no gamepad detected")
run = ret
while run:
time.sleep(0.1)
if msvcrt.kbhit() and msvcrt.getch() == chr(27).encode(): # detect ESC
run = False
ret, info = joystickapi.joyGetPosEx(id)
if ret:
btns = [(1 << i) & info.dwButtons != 0 for i in range(caps.wNumButtons)]
axisXYZ = [info.dwXpos-startinfo.dwXpos, info.dwYpos-startinfo.dwYpos, info.dwZpos-startinfo.dwZpos]
axisRUV = [info.dwRpos-startinfo.dwRpos, info.dwUpos-startinfo.dwUpos, info.dwVpos-startinfo.dwVpos]
if info.dwButtons:
print("buttons: ", btns)
if any([abs(v) > 10 for v in axisXYZ]):
print("axis:", axisXYZ)
if any([abs(v) > 10 for v in axisRUV]):
print("roation axis:", axisRUV)
print("end")
The api binding and example is provided in the GitHub repository python_windows_joystickapi
I have a USB controller that I'm trying to get inputs from, a Microsoft® SideWinder® Plug & Play Game Pad. I'm having difficulties trying to figure out how to receive its inputs correctly. Unfortunately, I cannot use pygame as it requires a window to receive inputs from, but I have to generate a pyglet window (via PsychoPy) to run my program. With pygame it can connect and show the state of buttons, but it cannot receive inputs without creating a window. I tried looking for other libraries, but all I encountered was Inputs, which isn't compatible with my controller (doesn't detect the device after installing). The controller itself works as I've tested it with an online gamepad tester. PsychoPy's joystick API is currently broken and does not work, so no luck there either.
I was really hoping anyone had advice on how to receive inputs from my controller/gamepad into my program?
For Windows you can use the WINMM.dll directly.
Use the ctypes library to load the dll (see Loading shared libraries). Use ctypes.WINFUNCTYPE to create prototypes for the function:
joyGetNumDevs
joyGetDevCapsW
joyGetPosEx
import ctypes
winmmdll = ctypes.WinDLL('winmm.dll')
# [joyGetNumDevs](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetnumdevs)
"""
UINT joyGetNumDevs();
"""
joyGetNumDevs_proto = ctypes.WINFUNCTYPE(ctypes.c_uint)
joyGetNumDevs_func = joyGetNumDevs_proto(("joyGetNumDevs", winmmdll))
# [joyGetDevCaps](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetdevcaps)
"""
MMRESULT joyGetDevCaps(UINT uJoyID, LPJOYCAPS pjc, UINT cbjc);
32 bit: joyGetDevCapsA
64 bit: joyGetDevCapsW
sizeof(JOYCAPS): 728
"""
joyGetDevCaps_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint)
joyGetDevCaps_param = (1, "uJoyID", 0), (1, "pjc", None), (1, "cbjc", 0)
joyGetDevCaps_func = joyGetDevCaps_proto(("joyGetDevCapsW", winmmdll), joyGetDevCaps_param)
# [joyGetPosEx](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetposex)
"""
MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji);
sizeof(JOYINFOEX): 52
"""
joyGetPosEx_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p)
joyGetPosEx_param = (1, "uJoyID", 0), (1, "pji", None)
joyGetPosEx_func = joyGetPosEx_proto(("joyGetPosEx", winmmdll), joyGetPosEx_param)
Create the python function joyGetNumDevs, joyGetDevCaps and joyGetPosEx which delegate to the DLL. And create classes for the types JOYCAPS respectively JOYINFOEX:
# joystickapi - joyGetNumDevs
def joyGetNumDevs():
try:
num = joyGetNumDevs_func()
except:
num = 0
return num
# joystickapi - joyGetDevCaps
def joyGetDevCaps(uJoyID):
try:
buffer = (ctypes.c_ubyte * JOYCAPS.SIZE_W)()
p1 = ctypes.c_uint(uJoyID)
p2 = ctypes.cast(buffer, ctypes.c_void_p)
p3 = ctypes.c_uint(JOYCAPS.SIZE_W)
ret_val = joyGetDevCaps_func(p1, p2, p3)
ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYCAPS(buffer))
except:
ret = False, None
return ret
# joystickapi - joyGetPosEx
def joyGetPosEx(uJoyID):
try:
buffer = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4))()
buffer[0] = JOYINFOEX.SIZE
buffer[1] = JOY_RETURNALL
p1 = ctypes.c_uint(uJoyID)
p2 = ctypes.cast(buffer, ctypes.c_void_p)
ret_val = joyGetPosEx_func(p1, p2)
ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYINFOEX(buffer))
except:
ret = False, None
return ret
JOYERR_NOERROR = 0
JOY_RETURNX = 0x00000001
JOY_RETURNY = 0x00000002
JOY_RETURNZ = 0x00000004
JOY_RETURNR = 0x00000008
JOY_RETURNU = 0x00000010
JOY_RETURNV = 0x00000020
JOY_RETURNPOV = 0x00000040
JOY_RETURNBUTTONS = 0x00000080
JOY_RETURNRAWDATA = 0x00000100
JOY_RETURNPOVCTS = 0x00000200
JOY_RETURNCENTERED = 0x00000400
JOY_USEDEADZONE = 0x00000800
JOY_RETURNALL = (JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | \
JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | \
JOY_RETURNPOV | JOY_RETURNBUTTONS)
# joystickapi - JOYCAPS
class JOYCAPS:
SIZE_W = 728
OFFSET_V = 4 + 32*2
def __init__(self, buffer):
ushort_array = (ctypes.c_uint16 * 2).from_buffer(buffer)
self.wMid, self.wPid = ushort_array
wchar_array = (ctypes.c_wchar * 32).from_buffer(buffer, 4)
self.szPname = ctypes.cast(wchar_array, ctypes.c_wchar_p).value
uint_array = (ctypes.c_uint32 * 19).from_buffer(buffer, JOYCAPS.OFFSET_V)
self.wXmin, self.wXmax, self.wYmin, self.wYmax, self.wZmin, self.wZmax, \
self.wNumButtons, self.wPeriodMin, self.wPeriodMax, \
self.wRmin, self.wRmax, self.wUmin, self.wUmax, self.wVmin, self.wVmax, \
self.wCaps, self.wMaxAxes, self.wNumAxes, self.wMaxButtons = uint_array
# joystickapi - JOYINFOEX
class JOYINFOEX:
SIZE = 52
def __init__(self, buffer):
uint_array = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4)).from_buffer(buffer)
self.dwSize, self.dwFlags, \
self.dwXpos, self.dwYpos, self.dwZpos, self.dwRpos, self.dwUpos, self.dwVpos, \
self.dwButtons, self.dwButtonNumber, self.dwPOV, self.dwReserved1, self.dwReserved2 = uint_array
See the simple example to test the API:
import joystickapi
import msvcrt
import time
print("start")
num = joystickapi.joyGetNumDevs()
ret, caps, startinfo = False, None, None
for id in range(num):
ret, caps = joystickapi.joyGetDevCaps(id)
if ret:
print("gamepad detected: " + caps.szPname)
ret, startinfo = joystickapi.joyGetPosEx(id)
break
else:
print("no gamepad detected")
run = ret
while run:
time.sleep(0.1)
if msvcrt.kbhit() and msvcrt.getch() == chr(27).encode(): # detect ESC
run = False
ret, info = joystickapi.joyGetPosEx(id)
if ret:
btns = [(1 << i) & info.dwButtons != 0 for i in range(caps.wNumButtons)]
axisXYZ = [info.dwXpos-startinfo.dwXpos, info.dwYpos-startinfo.dwYpos, info.dwZpos-startinfo.dwZpos]
axisRUV = [info.dwRpos-startinfo.dwRpos, info.dwUpos-startinfo.dwUpos, info.dwVpos-startinfo.dwVpos]
if info.dwButtons:
print("buttons: ", btns)
if any([abs(v) > 10 for v in axisXYZ]):
print("axis:", axisXYZ)
if any([abs(v) > 10 for v in axisRUV]):
print("roation axis:", axisRUV)
print("end")
The api binding and example is provided in the GitHub repository python_windows_joystickapi
I'm trying to create a choose folder-dialog from Python, but I'm having problems setting the initial folder path for the dialog. I think it's a problem converting the string to a LPARAM when I'm setting the _BROWSEINFO structure.
bi.lParam = cast(path.encode("ascii", "ignore"), POINTER(LPARAM)).contents
I'm getting this error in the callback:
windll.user32.SendMessageA(handle, _BFFM_SETSELECTIONA, 1, lpdata)
ctypes.ArgumentError: argument 4: <class 'OverflowError'>: int too long to convert
Below is the code I'm using, it seems to work well except the call to SendMessageA.
I'm setting the _BROWSEINFO structure in browse_folder,
_WM_USER = 0x400
_BFFM_INITIALIZED = 1
_BFFM_SETSELECTIONA = _WM_USER + 102
_BFFM_SETSELECTIONW = _WM_USER + 103
_BIF_RETURNONLYFSDIRS = 0x00000001
_BIF_NEWDIALOGSTYLE = 0x00000040
_BFFCALLBACK = WINFUNCTYPE(None, HWND, UINT, LPARAM, LPARAM)
def _browse_callback(handle, umsg, lparam, lpdata):
if(umsg == _BFFM_INITIALIZED):
if(lpdata is not None):
windll.user32.SendMessageA(handle, _BFFM_SETSELECTIONA, 1, lpdata)
return 0
class _SHITEMID(Structure):
_fields_ = [
("cb", USHORT),
("abID", BYTE)]
class _ITEMIDLIST(Structure):
_fields_ = [
("mkid", POINTER(_SHITEMID))]
class _BROWSEINFO(Structure):
_fields_ = [
("hwndOwner", HWND),
("pidlRoot", UINT),
("pszDisplayName", LPCSTR),
("lpszTitle", LPCSTR),
("ulFlags", UINT),
("lpfn", _BFFCALLBACK),
("lParam", LPARAM),
("iImage", INT)]
def browse_folder(path, message):
display_name = create_string_buffer(MAX_PATH)
end_path = create_string_buffer(MAX_PATH)
pidl_root = _ITEMIDLIST()
bi = _BROWSEINFO()
bi.hwndOwner = 0
bi.pidlRoot = 0
bi.pszDisplayName = addressof(display_name)
bi.lpszTitle = message.encode("ascii", "ignore")
bi.ulFlags = _BIF_RETURNONLYFSDIRS | _BIF_NEWDIALOGSTYLE
bi.lpfn = _BFFCALLBACK(_browse_callback)
bi.lParam = cast(path.encode("ascii", "ignore"), POINTER(LPARAM)).contents
bi.iImage = 0
pidl = windll.shell32.SHBrowseForFolder(addressof(bi))
print(display_name.value)
windll.shell32.SHGetPathFromIDList(pidl, addressof(end_path))
print(repr(end_path.value))
I switched to unicode and the wide character type as suggested by the comments.
The problem was casting it to an LPARAM and then dereferencing, but the comments led me to the method from_buffer on LPARAM.
I set lParam as follows:
bi.lParam = LPARAM.from_buffer(c_wchar_p(path))
I've been following the WinUsb MSDN HowTo here and so far it's worked pretty well.
I've made it to the point in the guide where I have to call the WinUsb_Initialize() function from the WinUsb DLL. And that's where I'm stuck. My code so far is this, and you will doubtlessly ascertain that I've ripped a great deal of it screaming from the WinAPI.py module of PyWinUSB. Insert gratuitous thanks and giving credit to the creator of PyWinUSB here.
import ctypes
import platform
from ctypes import byref, POINTER, Structure, sizeof, c_ulong
from ctypes.wintypes import DWORD, WORD, BYTE, HANDLE, LPCWSTR, ULONG, WCHAR, \
BOOL
TCHAR = WCHAR
winUSB = ctypes.windll.winusb
winHID = ctypes.windll.hid
kernel32 = ctypes.windll.kernel32
setupAPI = ctypes.windll.setupapi
GENERIC_READ = (-2147483648)
GENERIC_WRITE = (1073741824)
FILE_SHARE_READ = 1
FILE_SHARE_WRITE = 2
OPEN_EXISTING = 3
FILE_FLAG_OVERLAPPED = 1073741824
if platform.architecture()[0].startswith('64'):
WIN_PACK = 8
else:
WIN_PACK = 1
class GUID(ctypes.Structure):
"""GUID Windows OS Structure"""
_pack_ = 1
_fields_ = [("data1", DWORD),
("data2", WORD),
("data3", WORD),
("data4", BYTE * 8)]
def __init__(self, data1 = None, data2 = None, data3 = None, data4 = None):
if data1 is not None:
self.data1 = data1
if data2 is not None:
self.data2 = data2
if data3 is not None:
self.data3 = data3
if data4 is not None:
self.data4 = data4
class SP_DEVICE_INTERFACE_DATA(Structure):
"""
typedef struct _SP_DEVICE_INTERFACE_DATA {
DWORD cbSize;
GUID InterfaceClassGuid;
DWORD Flags;
ULONG_PTR Reserved;
} SP_DEVICE_INTERFACE_DATA, *PSP_DEVICE_INTERFACE_DATA
"""
_pack_ = WIN_PACK
_fields_ = [("cb_size", DWORD),
("interface_class_guid", GUID),
("flags", DWORD),
("reserved", POINTER(ULONG))]
def __init__(self):
self.cb_size = sizeof(SP_DEVICE_INTERFACE_DATA)
class SP_DEVICE_INTERFACE_DETAIL_DATA(Structure):
"""
typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
DWORD cbSize;
TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;
"""
_pack_ = WIN_PACK
_fields_ = [("cb_size", DWORD),
("device_path", TCHAR * 1)] #device_path[1]
def __init__(self):
self.cb_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)
def get_string(self):
"""Retreive stored string"""
return ctypes.wstring_at(byref(self, sizeof(DWORD)))
class SP_DEVINFO_DATA(Structure):
"""
typedef struct _SP_DEVINFO_DATA {
DWORD cbSize;
GUID ClassGuid;
DWORD DevInst;
ULONG_PTR Reserved;
} SP_DEVINFO_DATA, *PSP_DEVINFO_DATA;
"""
_pack_ = WIN_PACK
_fields_ = [("cb_size", DWORD),
("class_guid", GUID),
("dev_inst", DWORD),
("reserved", POINTER(ULONG))]
def __init__(self):
self.cb_size = sizeof(SP_DEVINFO_DATA)
class DIGCF:
"""
Flags controlling what is included in the device information set
built by SetupDiGetClassDevs
"""
DEFAULT = 0x00000001
PRESENT = 0x00000002
ALLCLASSES = 0x00000004
PROFILE = 0x00000008
DEVICEINTERFACE = 0x00000010
def GetHidGuid():
"Get system-defined GUID for HIDClass devices"
hid_guid = GUID()
winHID.HidD_GetHidGuid(byref(hid_guid))
return hid_guid
SetupDiGetClassDevs = setupAPI.SetupDiGetClassDevsW
SetupDiGetClassDevs.restype = HANDLE
SetupDiGetClassDevs.argtypes = [
# __in_opt const GUID *ClassGuid,
# __in_opt PCTSTR Enumerator,
# __in_opt HWND hwndParent,
# __in DWORD Flags,
POINTER(GUID),
LPCWSTR,
HANDLE,
DWORD]
SetupDiGetDeviceInterfaceDetail = setupAPI.SetupDiGetDeviceInterfaceDetailW
SetupDiGetDeviceInterfaceDetail.restype = BOOL
SetupDiGetDeviceInterfaceDetail.argtypes = [
# __in HDEVINFO DeviceInfoSet,
# __in PSP_DEVICE_INTERFACE_DATA DeviceIn,
# __out_opt PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,
# __in DWORD DeviceInterfaceDetailDataSize,
# __out_opt PDWORD RequiredSize
# __out_opt PSP_DEVINFO_DATA DeviceInfoData
HANDLE,
POINTER(SP_DEVICE_INTERFACE_DATA),
POINTER(SP_DEVICE_INTERFACE_DETAIL_DATA),
DWORD,
POINTER(DWORD),
POINTER(SP_DEVINFO_DATA)]
devGuid = GUID(0x2518E22E, 0x0F35, 0x11E2, (BYTE * 8)(0xB1, 0xD5, 0x6C, 0xF0,
0x49, 0x73, 0x78, 0x72))
classGuid = GetHidGuid()
#GUID(0x745A17A0, 0x74D3, 0x11D0, (BYTE * 8)(0xB6, 0xFE, 0x00, 0xA0,
# 0xC9, 0x0F, 0x57, 0xDA))
derp = SetupDiGetClassDevs(
byref(classGuid), None, None,
(DIGCF.PRESENT | DIGCF.DEVICEINTERFACE))
def stepDevices(herp, index = 0):
devInterfaceData = SP_DEVICE_INTERFACE_DATA()
if setupAPI.SetupDiEnumDeviceInterfaces(
herp, None, byref(classGuid), index, byref(devInterfaceData)):
yield devInterfaceData
del devInterfaceData
else:
print(kernel32.GetLastError())
def enumDevices(herp):
index = 0
devInterfaceData = SP_DEVICE_INTERFACE_DATA()
while setupAPI.SetupDiEnumDeviceInterfaces(
herp, None, byref(classGuid), index, byref(devInterfaceData)):
print(kernel32.GetLastError())
yield devInterfaceData
index += 1
del devInterfaceData
def getDetail(herp, devData, devInfo = None):
reqSize = c_ulong(0)
devDetail = SP_DEVICE_INTERFACE_DETAIL_DATA()
SetupDiGetDeviceInterfaceDetail(
herp, byref(devData), None, 0, byref(reqSize), None)
ctypes.resize(devDetail, reqSize.value)
SetupDiGetDeviceInterfaceDetail(
herp, byref(devData), byref(devDetail),
reqSize, None, byref(devInfo))
return devDetail.get_string()
dev = stepDevices(derp).next()
devInfo = SP_DEVINFO_DATA()
devString = getDetail(derp, dev, devInfo)
winUSBHandle = kernel32.CreateFileW(
devString, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None)
#winUSB.WinUsb_Initialize(winUSBHandle, <what goes here?>)
I've tried creating a simple handle and passing that byref(handle) as the second parameter to the function, but the handle value stays as None.
I don't know how much has been done with Python and WinUSB.DLL but if anyone can point me in the correct direction I'd be sincerely grateful.
Thanks in advance.
The documentation says it is a PWINUSB_INTERFACE_HANDLE, which is a PVOID, which is a c_void_p in ctypes, so this should work:
h = ctypes.c_void_p()
winUSB.WinUsb_Initialize(winUSBHandle, ctypes.byref(h))
Does the winUSBHandle look valid (not 0 or -1 or 0xFFFFFFFF)?
Edit
If it is failing, make sure to use the ctypes copy of GetLastError or it may be incorrect. Make the library references with:
winUSB = ctypes.WinDLL('winusb',use_last_error=True)
and use:
ctypes.get_last_error()