如何在Windows中从USB HID设备获取原始描述符数据

本文关键字:获取 原始 描述 数据 HID Windows 中从 USB | 更新日期: 2023-09-27 18:25:28

如何在Windows中从HID设备获取原始描述符数据?

背景:

我需要从Windows中的HID设备中获取制造商、产品名称和序列号。我正在使用hid.dll访问使用此处所示功能的设备。我的问题和这个很相似。我可以从一些HID设备中获取制造商字符串和产品字符串,但大多数设备都无法返回这些数据,HidD_GetManufacturerString返回false。然而,我知道这些设备的描述符中确实有字符串信息,因为我可以使用USBTreeView看到它。

有趣的是,即使对于返回制造商和产品名称的设备,我通过hid.dll获得的值也与我使用上述工具看到的值非常不同,该工具从USB设备获取原始数据。

例如,Xbox 360控制器:

Via USB Tree View:
Device Description       : Xbox 360 Controller for Windows
Language 0x0409          : "©Microsoft Corporation"
iProduct                 : 0x02
Language 0x0409          : "Controller"
iSerialNumber            : 0x03
Language 0x0409          : "0843806"
Via hid.dll using HidD_GetManufacturerString, HidD_GetProductString, and HidD_GetSerialNumberString:
Description              : HID-compliant game controller
Product                  : Controller (XBOX 360 Controller for Windows)
Manufacturer             : FAILS
Serial Number            : FAILS

WinUSB根本无法打开这些设备来检索这些数据,因为它们不使用WinUSB.sys驱动程序。

1) 我不明白为什么HidD函数返回的值与USB描述符中的值不匹配。2) 我找不到任何方法来访问HID设备的原始USB描述符数据,因为我无法使用WinUSB访问它们。


编辑1:

好吧,所以我学到了更多关于HID的知识。我通过hid.dll获得的数据似乎是驱动程序指定的数据,而不是来自USB设备的数据。HID也可以应用于USB以外的传输设备。所以没关系。最后,我真正想知道的是,当我有HID设备时,我如何获得USB设备,以及我使用什么API。除了WinUSB不起作用之外,我唯一能找到的就是内核级别的函数IOCTL。我不知道这是否适合一个正常的、非管理员的应用程序。

如何在Windows中从USB HID设备获取原始描述符数据

我终于找到了解决方案。主要问题只是将HID设备与其父USB设备相关联。这是一个基本过程:

假设您已经拥有HID设备和SP_DEVINFO_DATA:

  1. 枚举所有USB设备,如图所示
  2. 查找带有CM_GetChild和CM_GetSibling的USB设备的所有子级
  3. 将已知HID设备的实例句柄(SP_DEVINFO_DATA->DevInst)与CM函数返回的每个子设备的实例手柄进行比较,以确定哪个USB设备是父设备
  4. 从那里,你可以获得任何你想要的USB信息,包括描述符

我在这里做到了。有很多事情你需要记住,比如复合USB设备支持。主要算法是这样的:

std::string usbHubInterface;
std::string compositeDeviceInstanceId;
for (std::string deviceInstanceId = GetParentDevice(hidDeviceInstanceId); !deviceInstanceId.empty(); deviceInstanceId = GetParentDevice(deviceInstanceId))
{
    std::string usbDeviceInterface = GetDeviceInterface(deviceInstanceId, &GUID_DEVINTERFACE_USB_DEVICE);
    if (!usbDeviceInterface.empty())
    {
        m_DeviceInterfacePath = usbDeviceInterface;
    }
    std::string usbHub = GetDeviceInterface(deviceInstanceId, &GUID_DEVINTERFACE_USB_HUB);
    if (!usbHub.empty())
    {
        usbHubInterface = usbHub;
        break;
    }
    // May be composite USB device. Save it for later use.
    if (usbDeviceInterface.empty()) 
    {
        compositeDeviceInstanceId = deviceInstanceId;
    }
}
if (usbHubInterface.empty())
{
    DBGPRINT("UsbDevice: cannot get parent USB hub interface");
    return;
}
if (!m_DeviceInterfacePath.empty())
{
    m_DeviceInstanceId = GetDeviceFromInterface(m_DeviceInterfacePath);
    DEVINST devNodeHandle = OpenDevNode(m_DeviceInstanceId);
    // Get device index in parent USB hub
    // https://learn.microsoft.com/windows-hardware/drivers/ddi/wdm/ns-wdm-_device_capabilities#usb
    m_UsbPortIndex = PropertyDataCast<ULONG>(GetDevNodeProperty(devNodeHandle, &DEVPKEY_Device_Address, DEVPROP_TYPE_UINT32));
    // Composite USB device
    if (IsCompositeUSBDevice(m_DeviceInstanceId))
    {
        // Need to acquire interface number in parent USB device
        // https://learn.microsoft.com/windows-hardware/drivers/usbcon/usb-common-class-generic-parent-driver
        if (!GetInterfaceNumber(compositeDeviceInstanceId, m_UsbInterfaceNumber))
        {
            DBGPRINT("UsbDevice: cannot get interface number from composite USB device");
            return;
        }
    }
}
else
{
    DBGPRINT("UsbDevice: cannot get parent USB device interface");
    return;
}
// Open device handle first to wake up device from S3 suspend state
ScopedHandle usbInterfaceHandle = OpenDeviceInterface(m_DeviceInterfacePath);
ScopedHandle hubInterfaceHandle = OpenDeviceInterface(usbHubInterface, true);
USB_DEVICE_DESCRIPTOR deviceDescriptor;
if (!GetDeviceDescriptor(hubInterfaceHandle, m_UsbPortIndex, deviceDescriptor))
    return;
m_VendorId = deviceDescriptor.idVendor;
m_ProductId = deviceDescriptor.idProduct;
m_VersionNumber = deviceDescriptor.bcdDevice;
// Assume that we are always using first configuration
const UCHAR configurationIndex = 0;
if (!GetFullConfigurationDescriptor(hubInterfaceHandle, m_UsbPortIndex, configurationIndex, m_ConfigurationDescriptor))
    return;
// Search for interface descriptor
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = nullptr;
if (!SearchInterfaceDescriptor(m_ConfigurationDescriptor, m_UsbInterfaceNumber, interfaceDescriptor))
    return;
std::wstring stringBuffer;
// Get the array of supported Language IDs, which is returned in String Descriptor 0
if (!GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, 0, 0, stringBuffer))
    return;
// Use first supported language
USHORT languageID = stringBuffer[0];
if (GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, deviceDescriptor.iManufacturer, languageID, stringBuffer))
    m_Manufacturer = utf8::narrow(stringBuffer);
// Get interface name instead of whole product name, if present
UCHAR productStringIndex = interfaceDescriptor->iInterface ? interfaceDescriptor->iInterface : deviceDescriptor.iProduct;
if (GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, productStringIndex, languageID, stringBuffer))
    m_Product = utf8::narrow(stringBuffer);
if (GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, deviceDescriptor.iSerialNumber, languageID, stringBuffer))
    m_SerialNumber = utf8::narrow(stringBuffer);
// Get HID Descriptor
PHID_DESCRIPTOR hidDescriptor = nullptr;
if (!GetHidDescriptor(interfaceDescriptor, hidDescriptor))
    return;
// Get raw HID Report Descriptor
if (!GetHidReportDescriptor(hubInterfaceHandle, m_UsbPortIndex, hidDescriptor->DescriptorList[0].wReportLength, interfaceDescriptor->bInterfaceNumber, m_HidReportDescriptor))
{
    DBGPRINT("UsbDevice: cannot get raw HID Report Descriptor");
}