HID stands for Human Interface Device.
Let’s say there is a HID device connected to PC.
This device has no vendor driver but it is registered as a known HID class device so Windows
uses Microsoft supplied driver to handle the device as it is specified in Device Class Definition for HID 1.11.
There are two ways to read HID device input reports.
One way is to use ReadFile and the another way is to use a function from HID.DLL which is called HidD_GetInputReport.
I was successful with ReadFile however HidD_GetInputReport function returned some mysterious data which I cannot identify so far.
Here is how I understand the necessary steps for opening a HID device.

At the first step we need to load HID.DLL library and import few useful functions.
HMODULE hHidLib;
//function pointers to HID.DLL proc addresses
void (__stdcall *HidD_GetHidGuid)(OUT LPGUID HidGuid);
bool (__stdcall *HidD_GetInputReport)(IN HANDLE HidDeviceObject, \
IN OUT PVOID ReportBuffer, IN ULONG ReportBufferLength);
hHidLib = LoadLibrary("HID.DLL");
if (!hHidLib) OnError("Failed to load HID.DLL.");
(FARPROC&) HidD_GetHidGuid = GetProcAddress(hHidLib, "HidD_GetHidGuid");
(FARPROC&) HidD_GetInputReport = GetProcAddress(hHidLib, "HidD_GetInputReport");
if ( !HidD_GetInputReport || !HidD_GetHidGuid){
OnError("Couldn’t find one or more HID functions.");
FreeLibrary(hHidLib);
}
Next we should get get system registered GUID for HID class using imported function.
//get GUID for HID devices (HidD_GetHidGuid)(&guid);
The task now is to iterate through all HID devices so we can find the needed device.
For this reason device information set should be created:
HDEVINFO hDevInfo;
hDevInfo = SetupDiGetClassDevs( &guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if (hDevInfo == INVALID_HANDLE_VALUE){
FreeLibrary(hHidLib);
OnError("Failed to open handle to HID device set.");
}
Device information set is the handle to a set of interfaces of all devices of a specified class (GUID specifies device class).
Interface data of each device contains GUID of the registered device interface.
With this GUID it is possible to get device path which is needed for CreateFile function.
HANDLE hDevice = INVALID_HANDLE_VALUE;
DWORD dwIndex = 0;
// contains interface GUID
SP_DEVICE_INTERFACE_DATA devData;
// points to struct which contains device path
PSP_DEVICE_INTERFACE_DETAIL_DATA devDetailData = NULL;
//set prior to calling
devData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
DWORD devDetailDataSize;
DWORD ReqSize;
//iterate through all devices of a specified class
while(SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guid, dwIndex, &devData)){
dwIndex++;
//probing to get allocation size
SetupDiGetDeviceInterfaceDetail(hDevInfo, &devData, NULL, 0, &devDetailDataSize, NULL);
//allocating space for interface detail data which contains device path
devDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(devDetailDataSize);
//set prior to calling next function
devDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
//getting DevicePath in interface detail data struct
if (! SetupDiGetDeviceInterfaceDetail (
hDevInfo, &devData, devDetailData, devDetailDataSize, &ReqSize,
NULL)){
SetupDiDestroyDeviceInfoList(hDevInfo);
return FALSE;
}
//at this point we have device path and
//so it is possible to check for PID and VID values
//which are usually contained in device path string
//test vid and pid to find our device
if (NULL != strstr(devDetailData->DevicePath, "vid_099d&pid_ff00")){
//opening handle for a device
hDevice = CreateFile(devDetailData->DevicePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
break;
};
}//end of while
So in the cycle shown above we just found our HID device and opened its handle.
Now it is time for some I/O operations but before that we need to check for errors and do some freeing.
Note that when using ReadFile the first byte of a buffer should contain ID of the report which will be fetched by driver.
//delete device information set
SetupDiDestroyDeviceInfoList(hDevInfo);
if (hDevice == INVALID_HANDLE_VALUE){
FreeLibrary(hHidLib);
OnError("Failed to find or open device");
}
//working with device
DWORD dwBytes;
unsigned char pBuf[BUFFER_LENGTH];
//first byte specifies report ID
pBuf[0] = 0;
ReadFile(hDevice, pBuf, BUFFER_LENGTH, &dwBytes, NULL);
Another way of reading report data is shown below but for my test device with MS supplied driver it returns permanent data of unknown origin so I am not sure if I am using it correctly.
dwBytes=BUFFER_LENGTH;
pBuf[0] = 0 ;
if (! (HidD_GetInputReport)(hDevice, pBuf, dwBytes)){
GetLastError();
};
Don’t forget to unload the library and close the handle when you are done with I/O.
//closing device handle CloseHandle(hDevice); FreeLibrary(hHidLib);
Some crude source code example for this post can be found here.
To perform some real I/O with HID device it seems useful to open a polling thread which will cycle with ReadFile to read HID reports regularly.
More information and some examples are available in Windows DDK package.
See HCLIENT sources somewhere at \NTDDK\src\wdm\hid\hclient\ on your drive.
Hi Alexander, I have a small device that enumerates as a standard HID device. I have emplemented your code and have successfully acquired a file handle to the HID device using CreateFile().
However, when I try to read the input report using ReadFile(), I get ERROR_INVALID_USER_BUFFER #1784.
I am wondering if my device descriptor is not exactlly correct and perhaps you can verify it or give me some advice as to what is my problem.
I am not sure also if I should be setting the HID descriptor to BULK or Interrupt and what the SubClass and Protocol values should be. For now I have them set to BULK and zero for the latter settings. Any advice on these would also be helpful.
My PC is running 32Bit Windows XP SP3, and Device Manager successfully enumerates my device as a HID Compliant Device.
Here is my report descriptor.
__align(4) const uint08 ReportDescriptor_POPAddress[16] =
{
0×06, 0×00, 0xFF, //USAGE_PAGE (Generic Deivce)
0×09, 0×01, //USAGE (Vendor Usage)
0xA1, 0×01, //Collection Application
0×09, 0×02, //USAGE (Vendor Usage)
0×75, 0×08, //Report Size (8)
0×95, 0×01, //Report Count (1)
0×81, 0×02, //Input: Data, Variable, Absolute
0xC0 //END_COLLECTION
};
unsigned int numBytes = 0;
unsigned char popdata[32];
popdata[0] = 0;
if (!ReadFile(file, &popdata, 1, &numBytes, NULL))
{
printf(“Error in ReadFile: %x”, GetLastError());
CloseHandle(file);
return 0;
}
I also set the number of bytes to read to 2 but the ReadFile never returns.
Any help would be much appreciated.