Hid.dll导出函数HidD_GetPreparsedData
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数据结构复制给应用层。