1

I'm working with Python and trying to talk to this simple USB device using WinUSB (from Microsoft). I'm having a problem since in order to find the device, calling the setupAPI function SetupDiEnumDeviceInterfaces takes a struct object, which I defined as a class:

class _SP_DEVINFO_DATA:
    def __init__(self, ClassGUID, DevInst = ""):        
        '''flags = SPINT_DEFAULT, SPINT_REMOVED, or SPINT_ACTIVE'''
        self._ClassGUID = None
        self._DevInst = None
        self._Reserved = None
        self._cbSize = None

    ###Getters:
    def getClassGUID(self):
        return self._ClassGUID
    def getDevInst(self):
        return self._DevInst
    def getReserved(self):
        return self._Reserved
    def getcbSize(self):
        return self._cbSize

    ###Setters:
    def setClassGUID(self, value):
        self._ClassGUID = value
    def setDevInst(self, value):
        self._DevInst = value
    def setReserved(self, value):
        self._Reserved = value
    def setcbSize(self):
        self._cbSize = sys.getsizeinfo(self)       

    ClassGUID = property(getClassGUID, setClassGUID, None, "Class GUID")
    DevInst = property(getDevInst, setDevInst, None, "Device Instance")
    Reserved = property(getReserved, setReserved, None, "RESERVED: DO NOT USE")
    cbSize = property(getcbSize, setcbSize, None, "CB Size. Set automatically")

I tried using the property because it gave me the error :

<type 'exceptions.TypeError'>: Don't know how to convert parameter

otherwise, and I'd read that defining parameters like this would resolve the problem, but it doesn't, and I'm not certain what to do here.

I want to use WinUSB because I only need to read from the device and write to the device, and that's it, and WinUSB seems to have what I need, but until I can get past this problem, I'm kind of stuck

Any suggestions? How do I pass a class object to a DLL function loaded with ctypes.windll.LoadLibrary(DLL)?

And if there's an easier way to do this, I'm all for that too.

Thanks.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
Will
  • 281
  • 1
  • 6
  • 18

2 Answers2

2

As @Roland said, you must derive from ctypes.Structure. Here's a working version:

import ctypes
from ctypes import wintypes
import uuid

class _SP_DEVINFO_DATA(ctypes.Structure):
    _fields_ = [("cbSize", wintypes.DWORD),
                ("ClassGuid", ctypes.c_char * 16),
                ("DevInst", wintypes.DWORD),
                ("Reserved", wintypes.LPVOID)]

    def __init__(self, guid, inst):
        self.cbSize = ctypes.sizeof(_SP_DEVINFO_DATA)
        self.ClassGuid = uuid.UUID(guid).get_bytes()
        self.DevInst = (ctypes.c_ulong)(inst)
        self.Reserved = None

    def __repr__(self):
        return "_SP_DEV_INFO_DATA(cbsize={},ClassGuid={},DevInst={})".format(
            self.cbSize,uuid.UUID(bytes=self.ClassGuid),hex(self.DevInst))

sp = _SP_DEVINFO_DATA('08751880-13bb-11e2-96f0-402cf4ca5e51',0x12345678)
print sp

Output:

_SP_DEV_INFO_DATA(cbsize=28,ClassGuid=08751880-13bb-11e2-96f0-402cf4ca5e51,DevInst=0x12345678L)
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Alright thank you. I am having another problem though, in that it succeeds (Kernel32.LastError = 0) when I try to run sAPI.SetupDiGetClassDevsExW(receiverGUID, None, None, "DIGCF_ALLCLASSES", None, None, None), however, all I get returned is an integer value that I have no idea what to do with. Is there a good resource that I could browse that would help me make use of that value? – Will Oct 11 '12 at 19:23
  • Also, how do I determine what I'm to put into the DevInst argument? I see you put a hex value in, albeit an arbitrary one, but is that what goes there? Where would I get that value from? Thanks – Will Oct 11 '12 at 19:30
  • `SetupDiGetClassDevs` returns a handle to a *device information set* for all the devices that belong to the device class you specified with the GUID. `SetupDiEnumDeviceInfo` is then used to query the set for the devices. You don't fill out the _SP_DEVINFO_DATA structure, `SetupDiEnumDeviceInfo` does. See the Microsoft docs. There are examples in C. http://msdn.microsoft.com/en-us/library/windows/hardware/ff550897(v=vs.85).aspx – Mark Tolonen Oct 11 '12 at 20:17
  • I thank you very much for your input, it has been of great help. If you could provide just a touch more, I'm getting an error value of 259 when I call SetupDiEnumDeviceInterfaces. I googled it and it said it means ERROR_NO_MORE_ITEMS. When I call the function, am I supposed to pass into it the class guid (HIDClass) or the device guid? Also, for reference, the value returned by SetupDiGetClassDevsWX (SetupDiGetClassDevs does not exist in this iteration of WinUSB) is just the integer value 2429888. Is it enough to just pass that along as well? Thanks again. – Will Oct 11 '12 at 23:08
  • Also, for some reason the passed guid into the object is being truncated. When I execute the uuid.UUID().get_bytes on the string outside the object, I get 'tZ\x17\xa0t\xd3\x11\xd0\xb6\xfe\x00\xa0\xc9\x0fW\xda', but in the object I only see 'tZ\x17\xa0t\xd3\x11\xd0\xb6\xfe'. I read that I have to pass to the the SetupDiEnumDeviceInfo function an initialized _SP_DEV_INFO_DATA object. What values should I be initializing this object with? – Will Oct 12 '12 at 00:17
  • SetupDiEnumDeviceInterfaces needs a device set generated by using the Flag DIGCF_DEVICEINTERFACE. The "class GUID" should be an interface GUID not a class GUID for it to return anything. – Mark Tolonen Oct 12 '12 at 01:05
  • SetupDiEnumDeviceInfo *returns* an SP_DEV_INFO_DATA object. You only have to init cbSize. – Mark Tolonen Oct 12 '12 at 01:08
  • You should ask these as new questions :) – Mark Tolonen Oct 12 '12 at 01:33
  • I will thanks. You seem to be a good resource, that's why I've been asking them here. So instead I'll just link the future questions I've asked. Like [this](http://stackoverflow.com/questions/12861619/what-do-i-pass-as-the-last-value-to-the-function-setupapi-dll-setupdienumdevicei) one here. – Will Oct 12 '12 at 14:54
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/17942/discussion-between-will-and-mark-tolonen) – Will Oct 12 '12 at 16:29
0

Your class should be derived from ctypes.Structure.

import ctypes

class _SP_DEVINFO_DATA(ctypes.Structure):

    _fields_ = [("cbSize", ctypes.c_ulong),
                ("ClassGuid", ctypes.c_ubyte * 12),
                ("DevInst", ctypes.c_ulong),
                ("Reserved", ctypes.c_voidp)]

    def __init__(self, guid, inst):
        self.cbSize = 24
        # GUID doesn't work like this... Needs proper conversion
        #self.ClassGuid = (ctypes.c_ubyte * 12)(bytearray(guid))
        self.DevInst = (ctypes.c_ulong)(inst)
        self.Reserved = None
Roland Smith
  • 42,427
  • 3
  • 64
  • 94
  • I'm getting an error about this line now: self.ClassGuid = (ctypes.c_ubyte * 12)(bytearray(guid)) It's telling me an integer is required. How do I convert the GUID into an integer value? Am I supposed to use the uuid module? I think this will work, thanks for the advice. I just have to figure out how to handle the GUID correctly. – Will Oct 11 '12 at 02:58
  • I'm thinking that I want to use the uuid.UUID() function to get an actual GUID type object from the literal values, but where do I go from there? – Will Oct 11 '12 at 13:59
  • `uuid.UUID().get_bytes()`. GUID is 16 bytes not 12 as well. – Mark Tolonen Oct 11 '12 at 15:44