2

How can I get the product string from USB Device Descriptor (device is not the hard drive, it is external device like headset) on C# or C++? I need to get "My Headset" string.

      ======================== USB Device ========================

        +++++++++++++++++ Device Information ++++++++++++++++++
Device Description       : USB Composite Device
Device Path              : \\?\usb#vid_2ae2&pid_1388#abcdef0123456789#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
Device ID                : USB\VID_2AE2&PID_1388\ABCDEF0123456789
Driver KeyName           : {36fc9e60-c465-11cf-8056-444553540000}\0018 (GUID_DEVCLASS_USB)
Driver                   : C:\Windows\System32\drivers\usbccgp.sys (Version: 6.3.9600.17238  Date: 2014-07-24)
Driver Inf               : C:\Windows\inf\usb.inf
Legacy BusType           : PNPBus
Class                    : USB
Service                  : usbccgp
Enumerator               : USB
Location Info            : Port_#0006.Hub_#0003
Location IDs             : PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(6), ACPI(_SB_)#ACPI(PCI0)#ACPI(XHC_)#ACPI(RHUB)#ACPI(HS06)
Container ID             : {acb9ebb8-b976-57c5-b90d-5ecc5e96e487}
Manufacturer Info        : (Standard USB Host Controller)
Capabilities             : 0x94 (Removable, UniqueID, SurpriseRemovalOK)
Address                  : 6
Problem Code             : 0
Power State              : D0 (supported: D0, D3, wake from D0)
 Child Device 1          : USB Input Device
  Device ID              : USB\VID_2AE2&PID_1388&MI_03\6&12E75DA&0&0003
  Class                  : HIDClass
   Child Device 1        : HID-compliant consumer control device
    Device ID            : HID\VID_2AE2&PID_1388&MI_03\7&178A021A&0&0000
    Class                : HIDClass
 Child Device 2          : USB Audio Device
  Device ID              : USB\VID_2AE2&PID_1388&MI_00\6&12E75DA&0&0000
  Class                  : MEDIA
   Child Device 1        : Audio Endpoint
    Device ID            : SWD\MMDEVAPI\{0.0.1.00000000}.{0B0B6598-290C-495B-A4E1-D6A633F61574}
    Class                : AudioEndpoint
   Child Device 2        : Audio Endpoint
    Device ID            : SWD\MMDEVAPI\{0.0.0.00000000}.{E3ADC851-586E-4FF8-BBF3-0EBF7E72FBDA}
    Class                : AudioEndpoint

        ------------------ Device Descriptor ------------------
bLength                  : 0x12 (18 bytes)
bDescriptorType          : 0x01 (Device Descriptor)
bcdUSB                   : 0x200 (USB Version 2.00)
bDeviceClass             : 0x00 (defined by the interface descriptors)
bDeviceSubClass          : 0x00
bDeviceProtocol          : 0x00
bMaxPacketSize0          : 0x40 (64 bytes)
idVendor                 : 0x2AE2
idProduct                : 0x1388
bcdDevice                : 0x1393
iManufacturer            : 0x00 (No String Descriptor)
iProduct                 : 0x02 (String Descriptor 2)
 Language 0x0409         : "My Headset"
iSerialNumber            : 0x03 (String Descriptor 3)
 Language 0x0409         : "ABCDEF0123456789"
bNumConfigurations       : 0x01

Currently I use this code sample to get product string, I open the hub and call DeviceIoControl with IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION to get product string of my device connected to this hub, but this code works from time to time because sometimes hub is opened by someone else and CreateFile(hubPath ..) fails:

// Open Hub
IntPtr handle = CreateFile(hubPath, FileAccess.ReadWrite, FileShare.ReadWrite,
    IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);

if (handle.ToInt32() != INVALID_HANDLE_VALUE)
{
    // 1. Load connection properties to get Device descriptor indexes
    LogWriter.Debug("checking device, load hub connection properties...");

    USB_NODE_CONNECTION_INFORMATION_EX connectionInfo = new USB_NODE_CONNECTION_INFORMATION_EX();
    connectionInfo.ConnectionIndex = port;

    // memset
    string NullString = new string((char)0, nBytes / Marshal.SystemDefaultCharSize);
    IntPtr connectionInfoBuffer = Marshal.StringToHGlobalAuto(NullString);
    Marshal.StructureToPtr(connectionInfo, connectionInfoBuffer, true);
    int iProductIndex = -1;

    int ioBytes = Marshal.SizeOf(connectionInfo);
    int nBytesReturned = 0;

    if (DeviceIoControl(handle,
        IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
        connectionInfoBuffer,
        ioBytes,
        connectionInfoBuffer,
        ioBytes,
        out nBytesReturned,
        IntPtr.Zero) != 0)
    {
        LogWriter.Debug("checking device, hub connection properties loaded");

        USB_NODE_CONNECTION_INFORMATION_EX nodeConnection = (USB_NODE_CONNECTION_INFORMATION_EX)Marshal.PtrToStructure(connectionInfoBuffer, typeof(USB_NODE_CONNECTION_INFORMATION_EX));

        if (nodeConnection.DeviceIsHub == 0)
        {
            iProductIndex = nodeConnection.DeviceDescriptor.iProduct;
            LogWriter.Debug(String.Format("GetDeviceProperties() Checking device, iProductIndex = {0}", iProductIndex));
        }
    }
    else
    {
        CheckError("DeviceIoControl");
    }

    // 2. Load iProduct descriptor
    USB_DESCRIPTOR_REQUEST stringDescReq = new USB_DESCRIPTOR_REQUEST();

    int bufSize = Marshal.SizeOf(stringDescReq) + MAXIMUM_USB_STRING_LENGTH;

    stringDescReq.ConnectionIndex = port;
    stringDescReq.SetupPacket.wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) | iProductIndex);
    stringDescReq.SetupPacket.wIndex = 1033; // Language code
    stringDescReq.SetupPacket.wLength = (short)(bufSize - Marshal.SizeOf(stringDescReq));

    // типа memset
    IntPtr ptrRequest = Marshal.StringToHGlobalAuto(NullString);
    Marshal.StructureToPtr(stringDescReq, ptrRequest, true);

    int nBytesRet = 0;

    LogWriter.Debug(String.Format("checking device, load USB descriptor({0}, {1}, {2})",
        stringDescReq.SetupPacket.wValue,
        stringDescReq.SetupPacket.wIndex,
        stringDescReq.SetupPacket.wLength));

    if (DeviceIoControl(handle,
        IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
        ptrRequest,
        bufSize,
        ptrRequest,
        bufSize,
        out nBytesRet,
        IntPtr.Zero) != 0)
    {
        int ptrSize = ptrRequest.ToInt32();

        LogWriter.Debug(String.Format("checking device, USB descriptor loaded({0}, {1})",
            ptrSize,
            Marshal.SizeOf(stringDescReq)));

        IntPtr ptrStringDesc = new IntPtr(ptrSize + Marshal.SizeOf(stringDescReq));
        USB_STRING_DESCRIPTOR StringDesc = (USB_STRING_DESCRIPTOR)Marshal.PtrToStructure(ptrStringDesc, typeof(USB_STRING_DESCRIPTOR));
        prop.ProductName = StringDesc.bString;

        if (prop.ProductName != null)
        {
            LogWriter.Debug("ProductName = " + prop.ProductName);
        }
    }
    else
    {
        LogWriter.Warn("DeviceIoControl failed");
        CheckError("DeviceIoControl");
    }

    CloseHandle(handle);
}

So I need other way to get product string that will work stable.

One note: the current device is HID, so using the functions for HID will be allowed, but I have also non-HID USB devices and it would be great if solution can work with HID and non-HID at the same time. I tried HidD_GetProductString routine year ago and as I remember it returned empty buffer (the problem can be the incorrect device path because HID has child devices and in this case the problem is how to get correct device path).

Evgeniy
  • 403
  • 1
  • 8
  • 18
  • If someone else has the hub open and `CreateFile()` fails, you can't query the hub directly. Simple as that. If the information is not available somewhere else, such as in the Registry, then you are out of luck. – Remy Lebeau Apr 05 '16 at 16:25
  • Take a look at this: http://stackoverflow.com/questions/3331043/get-list-of-connected-usb-devices – Gusman Apr 05 '16 at 16:54
  • I tried WMI but it doesn't return the usb product string (I tried a lot of classes there). – Evgeniy Apr 06 '16 at 12:11

1 Answers1

0

HidD_GetProductString is the way to go for HID devices. You might have encountered the \0 on every other byte before. If you're using HidLibrary, and have a HidDevice _device, you can get the product string like this (manufacturer works the same way):

byte[] bs;
_device.ReadProduct(out bs);
string ps = "";
foreach (byte b in bs) {
    if (b > 0)
        ps += ((char)b).ToString();
}
Jack Humbert
  • 129
  • 2
  • 9
  • 2
    The reason every other byte is 0 is presumably because it's a UTF-16 string. Your method of handling it would therefore incorrectly handle non-ASCII strings. – rdb Jan 30 '18 at 13:34