5

I have been able to enumerate USB devices using the SetupAPI, and I've looked at the usbview application from the WDK, but I still can't figure out how to get the USB_DEVICE_DESCRIPTOR.

  • I would rather avoid using WMI.
  • DeviceIoControl is what the sample app usbview uses, but that really only works if you're enumerating devices on a Hub. I suppose if I can get to the parent hub (and port) given a device path (or Id), this method may work but I haven't been able to determine how to do this either.
  • I have a mix of devices for which I'd like to get the descriptor. Some of these are HIDs, and it's possible some are WinUsb.sys devices. If they are WinUsb devices I can use WinUsb_GetDescriptor, but that won't work for HIDs (and I don't know how to tell the difference between them from the Id or Path...Interface class I guess?).
  • I could use SetupDiGetDeviceRegistryProperty but in the list of available properties, I can see the Manufacturer string, but not the vendor Id.
  • I could possibly parse this value from the device path or the device Id, but that seems sort of...hack-ish. Is that just what people do though? Also it still leaves me going to other methods if I want other fields like the Manufacturer, where if I could just get the whole USB_DEVICE_DESCRIPTOR I think I'd have about everything I need.
  • LibUsb.Net only supports WinUsb devices apparently. That's how it seems to get the descriptor.
  • Apparently WinRT has some new APIs and therefore Windows Store apps have a nice way of getting the descriptor. But this is definitely not a Windows Store app, and I don't know that there is another way to use the newer APIs.

Can anyone point me in the right direction? Is it just not possible to get this information from the WinAPI in a nice way without starting at the Hub?

Malaise
  • 705
  • 3
  • 8
  • 20
  • 1
    Seems possibly helpful: http://microsoft.public.development.device.drivers.narkive.com/HwMlQXV0/how-to-use-devices-found-with-usbview-ie-getting-a-handle-or-devicepath and `SetupDiGetDeviceProperty` with `DEVPKEY_Device_Address` – Ben Voigt Jan 20 '15 at 04:33
  • 3
    Or `CM_Get_DevNode_Property`. Once you have the port number, with `CM_Get_Parent` you can get the parent USB hub, open it, send it the IOCTL requesting more information on that port's enumerated device. – Ben Voigt Jan 20 '15 at 04:37
  • Can you mention the WinRT APIs? It could be possible to use them via C++/WRL for a native win32 application. – Sahil Singh Jun 05 '18 at 16:02

1 Answers1

1

Your best bet would be to extract the info from the device path and use the SetupDi functions to get the other bits and pieces. As far as I know, the device path always follows the same convention. i.e.:

"\\?\usb#vid_0000&pid_1111#SERIAL#{GUID}" where 0000 is the VID and 1111 is the PID as hex strings. SERIAL is either the serial provided by the hardware or the OS-assigned serial value.

I personally found an instance where I absolutely wanted to get the device descriptor in order to pull the serial that way. In some instances, the OS was not recognizing the serial number provided by my hardware. I fixed that on the hardware side, but I still wanted to accommodate old hardware on the PC side. Below is my approach. There may be something better, but this is the best that I have come up with so far. You may still consider it to be "hack-ish" though.

  1. Call SetupDiGetClassDevs() to setup your desired DeviceInfoSet
  2. Obtain your device info data using SetupDiEnumDeviceInfo()
  3. Call SetupDiGetDeviceRegistryProperty() with SPDRP_LOCATION_INFORMATION to get the location information. This string should look like "Port_#0001.Hub_#0001". Parse this string to get the port number where your device is located. (I assume that this value is base 10, but I haven't verified this yet)
  4. Call CM_Get_Parent() to get the device node pointer value of the parent (hub)
  5. Call SetupDiGetClassDevs() with the GUID of {0xf18a0e88, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8}} to get all of the hubs on your system. That GUID should be defined as GUID_DEVINTERFACE_USB_HUB in usbiodef.h.
  6. Iterate through the list of devices using SetupDiEnumDeviceInfo(). Stop once DevInst equals the value obtained in step 4.
  7. Call SetupDiGetDeviceInterfaceDetail() on the index found in step 6.
  8. Call CreateFile() on the DevicePath obtained in step 7.
  9. Call DeviceIoControl() using the file created in step 8 and the port number obtained in step 3 as your connection index.

-EDIT-

As Ben pointed out in the comments, you can skip steps 5, 6, and 7 by using CM_Get_Device_ID on the parent's dev node obtained in step 4. Change the slashes (\) in this string to pounds (#). Prepend "\\?\" and then append "#{f18a0e88-c30c-11d0-8815-00a0c906bed8}". Use that as your device path in step 8. This avoids iterating through all of the hub devices on your system :)

Tails86
  • 507
  • 5
  • 19
  • I think there's a better way to get from step 4 to step 6, but I can't remember what at the moment. – Ben Voigt Sep 17 '15 at 22:50
  • 1
    Maybe `CM_Get_Device_ID` on the `DEVNODE*` of the parent to get the device instance ID, followed by `SetupDiOpenDeviceInfo` which gets you to the `PSP_DEVINFO_DATA` that `SetupDiEnumDeviceInterfaces` requires – Ben Voigt Sep 17 '15 at 22:55
  • You may be on to something here, Ben. The value I get from CM_Get_Device_ID is "USB\VID_050D&PID_0237\7&85D56EA&0&5". The path should be "\\?\usb#vid_050d&pid_0237#7&85d56ea&0&5#{f18a0e88-c30c-11d0-8815-00a0c906bed8}". That path can be generated if we assume the hub GUID. – Tails86 Sep 17 '15 at 23:24
  • This is similar to the approach I took in the end. Except...I think as Ben says I did not have to get the list of all Hubs. Unfortunately I got what I need to working, handed it off, and haven't thought much about it since so I don't quite remember what I did with that part... – Malaise Sep 17 '15 at 23:38
  • You have to call DeviceIoControl with the code IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX for the hub handle, not with the device handle, right? – amilamad Jan 10 '19 at 07:49
  • In step 3, you can get the port number without parsing a string by using SetupDiGetDeviceProperty with DEVPKEY_Device_Address. In step 4, instead of using CM_Get_Parent to get a DEVINST for the hub, you can use SetupDiGetDeviceProperty with DEVPKEY_Device_Parent to its instance id. Pass the instance id as the enumerator parameter when calling SetupDiGetClassDevs in step 5. That will limit the returned hubs to one, simplifying step 6 into a simple call to SetupDiEnumDeviceInterfaces. – Billy Jun 26 '22 at 07:26