Windows动态库hid.dll
+ -

Hid.dll导出函数HidD_GetPreparsedData

2022-05-13 8 0

Hid.dll导出函数HidD_GetPreparsedData可以通过IOCTL获取一个PHIDP_PREPARSED_DATA的不透明数据结构。通过这个数据结构可以出报告描述符的相关信息。
如本人在开发HID调试工具HidTool.exe的代码如下:

do
{
     ...
     PHIDP_PREPARSED_DATA PreparsedData;
     if (!HidD_GetPreparsedData(tmp_DeviceHandle, &PreparsedData))
     {
         CloseHandle(tmp_DeviceHandle);
         printf("Cannot get the Preparsed Data...\n");
         continue;
     }

     if (!HidP_GetCaps(PreparsedData, &st.Capabilities))
     {
         CloseHandle(tmp_DeviceHandle);
         printf("Cannot get the Cap Data...\n");
         continue;
     }
 }while(0);

我们通过Hid.dll导出函数与IOCTL对应大全http://www.usbzh.com/article/detail-929.html 一节知道,该函数的内部实现与两个IOCTL有关,分别为IOCTL_HID_GET_COLLECTION_INFORMATION 和 IOCTL_HID_GET_COLLECTION_DESCRIPTOR。
我们通过REACTOS 0.4.13关于HID.C的源代码HidD_GetPreparsedData可以见其实现真容.
本人下载本地的reoactos源代码HID.C源文件路径为:

E:\reactos\ReactOS-0.4.13-src-2020-0731\ReactOS-0.4.13\dll\win32\hid\hid.c

HidD_GetPreparsedData的源代码如下:

HIDAPI
BOOLEAN WINAPI
HidD_GetPreparsedData(IN HANDLE HidDeviceObject,
                      OUT PHIDP_PREPARSED_DATA *PreparsedData)
{
  HID_COLLECTION_INFORMATION hci;
  DWORD RetLen;
  BOOLEAN Ret;

  if(PreparsedData == NULL)
  {
    return FALSE;
  }

  if(!DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_COLLECTION_INFORMATION,
                                       NULL, 0,
                                       &hci, sizeof(HID_COLLECTION_INFORMATION),
                                       &RetLen, NULL))
  {
    return FALSE;
  }

  *PreparsedData = LocalAlloc(LHND, hci.DescriptorSize);
  if(*PreparsedData == NULL)
  {
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
    return FALSE;
  }

  Ret = DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
                        NULL, 0,
                        *PreparsedData, hci.DescriptorSize,
                        &RetLen, NULL) != 0;

  if(!Ret)
  {
    /* FIXME - Free the buffer in case we failed to get the descriptor? */
    LocalFree((HLOCAL)*PreparsedData);
  }
#if 0
  else
  {
    /* should we truncate the memory in case RetLen < hci.DescriptorSize? */
  }
#endif

  return Ret;
}

IOCTL_HID_GET_COLLECTION_INFORMATION

从HidD_GetPreparsedData源代码可以看到,首先下发的是IOCTL_HID_GET_COLLECTION_DESCRIPTOR,获取集合描述符信息HID_COLLECTION_INFORMATION。我们在HID.DLL导出函数HidD_GetAttributes探究http://www.usbzh.com/article/detail-932.html 一节看到其内核实现。

CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription, PDODeviceExtension->CollectionNumber);

 ASSERT(CollectionDescription);
 //
 // init result buffer
 //
 CollectionInformation->DescriptorSize = CollectionDescription->PreparsedDataLength;
 CollectionInformation->Polled = CommonDeviceExtension->DriverExtension->DevicesArePolled;
 CollectionInformation->VendorID = CommonDeviceExtension->Attributes.VendorID;
 CollectionInformation->ProductID = CommonDeviceExtension->Attributes.ProductID;
 CollectionInformation->VersionNumber = CommonDeviceExtension->Attributes.VersionNumber;

可以看到根据HidClassPDO_GetCollectionDescription获取设备的集合,然后分别赋值:

  • Polled hidusb调用HidRegisterMinidriver时固定为FALSE
    Registration.DevicesArePolled = FALSE;
    
  • VendorID: 设备描述符中的idVendor
  • ProductID:设备描述符中的idProduct
  • VersionNumber:设备描述符中的bcdDevice
  • DescriptorSize:这里我们需要重点关注

这里通过HidClassPDO_GetCollectionDescription获取CollectionDescription。

PHIDP_COLLECTION_DESC
HidClassPDO_GetCollectionDescription(
    PHIDP_DEVICE_DESC DeviceDescription,
    ULONG CollectionNumber)
{
    ULONG Index;

    for(Index = 0; Index < DeviceDescription->CollectionDescLength; Index++)
    {
        if (DeviceDescription->CollectionDesc[Index].CollectionNumber == CollectionNumber)
        {
            //
            // found collection
            //
            return &DeviceDescription->CollectionDesc[Index];
        }
    }

    //
    // failed to find collection
    //
    DPRINT1("[HIDCLASS] GetCollectionDescription CollectionNumber %x not found\n", CollectionNumber);
    ASSERT(FALSE);
    return NULL;
}

所以PDODeviceExtension->CollectionNumber就是当前HID设备集合编号,其对应的结构体为:

typedef struct _HIDP_COLLECTION_DESC
{
   USAGE       UsagePage;
   USAGE       Usage;

   UCHAR       CollectionNumber;
   UCHAR       Reserved [15]; // Must be zero

   USHORT      InputLength;
   USHORT      OutputLength;
   USHORT      FeatureLength;
   USHORT      PreparsedDataLength;

   PHIDP_PREPARSED_DATA             PreparsedData;
} HIDP_COLLECTION_DESC, *PHIDP_COLLECTION_DESC;

这个可以看到比较清楚了,就是我们HID报告描述符中的一个COLLECTION的信息描述。
再回过头来看CollectionNumber,这个集合编号,在其生成HID的硬件ID时,也会用到:

   if (PDODeviceExtension->Common.DeviceDescription.CollectionDescLength > 1)
    {
        //
        // multi-tlc device
        //
        Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber, PDODeviceExtension->CollectionNumber) + 1;
        Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->CollectionNumber) + 1;
    }
    else
    {
        //
        // single tlc device
        //
        Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber) + 1;
        Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID) + 1;
    }

所以对于HID描述符,有多外应用集合时,其硬件ID会有COL信息,如果没有,就不需要。而这个CollectionNumber其实是hidparser解析报告描述符出来的:

for(Index = 0; Index < CollectionCount; Index++)
{
...
        DeviceDescription->ReportIDs[Index].CollectionNumber = Index + 1;
        DeviceDescription->ReportIDs[Index].ReportID = Index; //FIXME
        DeviceDescription->ReportIDs[Index].InputLength = HidParser_GetReportLength((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_INPUT);
        DeviceDescription->ReportIDs[Index].OutputLength = HidParser_GetReportLength((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_OUTPUT);
        DeviceDescription->ReportIDs[Index].FeatureLength = HidParser_GetReportLength((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_FEATURE);


        DeviceDescription->ReportIDs[Index].InputLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_INPUT) ? 1 : 0);
        DeviceDescription->ReportIDs[Index].OutputLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_OUTPUT) ? 1 : 0);
        DeviceDescription->ReportIDs[Index].FeatureLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_FEATURE) ? 1 : 0);

...

}

所以,其实PreparsedDataLength和PreparsedData是hidparser解析报告描述之后的数据结构

IOCTL_HID_GET_COLLECTION_DESCRIPTOR

IOCTL_HID_GET_COLLECTION_DESCRIPTOR功能比较交简单,就是根据长度将HIDParser解析出来的HIDP_COLLECTION_DESC数据结构复制给应用层。

0 篇笔记 写笔记

Hid.dll导出函数HidD_GetPreparsedData
Hid.dll导出函数HidD_GetPreparsedData可以通过IOCTL获取一个PHIDP_PREPARSED_DATA的不透明数据结构。通过这个数据结构可以出报告描述符的相关信息。如本人在开发HID调试工具HidTool.exe的代码如下:do{ ... PHID......
作者信息
USB中文网
B站搜索 站长漫谈 看视频。
pnpon内核开网,USB中文网,
busrom硬核技术网站长
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!