📄 usbhidiocdlg.cpp
字号:
//Free any resources used by previous API calls and still allocated.
//Setting ApplicationActive FALSE signals the ReadReport thread to close.
ApplicationActive=FALSE;
//Wait for the ReadReport thread to close.
WaitForSingleObject(ThreadHandle, INFINITE);
//Close open handles.
CloseHandle(DeviceHandle);
DisplayLastError("CloseHandle(DeviceHandle)");
CloseHandle(ReadHandle);
DisplayLastError("CloseHandle(ReadHandle)");
CloseHandle(ThreadHandle);
DisplayLastError("CloseHandle(ThreadHandle)");
CDialog::OnClose();
}
bool CUsbhidiocDlg::FindTheHID()
{
//Use a series of API calls to find a HID with a matching Vendor and Product ID.
HIDD_ATTRIBUTES Attributes;
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData;
SP_DEVICE_INTERFACE_DATA devInfoData;
bool LastDevice = FALSE;
int MemberIndex = 0;
bool MyDeviceDetected = FALSE;
LONG Result;
//These are the vendor and product IDs to look for.
//Uses Lakeview Research's Vendor ID.
const unsigned int VendorID = 0x0d8c;
const unsigned int ProductID = 0x000e;
Length = 0;
detailData = NULL;
DeviceHandle=NULL;
/*
API function: HidD_GetHidGuid
Get the GUID for all system HIDs.
Returns: the GUID in HidGuid.
*/
HidD_GetHidGuid(&HidGuid);
/*
API function: SetupDiGetClassDevs
Returns: a handle to a device information set for all installed devices.
Requires: the GUID returned by GetHidGuid.
*/
hDevInfo=SetupDiGetClassDevs \
(&HidGuid, \
NULL, \
NULL, \
DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
devInfoData.cbSize = sizeof(devInfoData);
//Step through the available devices looking for the one we want.
//Quit on detecting the desired device or checking all available devices without success.
MemberIndex = 0;
LastDevice = FALSE;
do
{
MyDeviceDetected=FALSE;
/*
API function: SetupDiEnumDeviceInterfaces
On return, MyDeviceInterfaceData contains the handle to a
SP_DEVICE_INTERFACE_DATA structure for a detected device.
Requires:
The DeviceInfoSet returned in SetupDiGetClassDevs.
The HidGuid returned in GetHidGuid.
An index to specify a device.
*/
Result=SetupDiEnumDeviceInterfaces \
(hDevInfo, \
0, \
&HidGuid, \
MemberIndex, \
&devInfoData);
if (Result != 0)
{
//A device has been detected, so get more information about it.
/*
API function: SetupDiGetDeviceInterfaceDetail
Returns: an SP_DEVICE_INTERFACE_DETAIL_DATA structure
containing information about a device.
To retrieve the information, call this function twice.
The first time returns the size of the structure in Length.
The second time returns a pointer to the data in DeviceInfoSet.
Requires:
A DeviceInfoSet returned by SetupDiGetClassDevs
The SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces.
The final parameter is an optional pointer to an SP_DEV_INFO_DATA structure.
This application doesn't retrieve or use the structure.
If retrieving the structure, set
MyDeviceInfoData.cbSize = length of MyDeviceInfoData.
and pass the structure's address.
*/
//Get the Length value.
//The call will return with a "buffer too small" error which can be ignored.
Result = SetupDiGetDeviceInterfaceDetail \
(hDevInfo, \
&devInfoData, \
NULL, \
0, \
&Length, \
NULL);
//Allocate memory for the hDevInfo structure, using the returned Length.
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length);
//Set cbSize in the detailData structure.
detailData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
//Call the function again, this time passing it the returned buffer size.
Result = SetupDiGetDeviceInterfaceDetail \
(hDevInfo, \
&devInfoData, \
detailData, \
Length, \
&Required, \
NULL);
//Open a handle to the device.
/*
API function: CreateFile
Returns: a handle that enables reading and writing to the device.
Requires:
The DevicePath in the detailData structure
returned by SetupDiGetDeviceInterfaceDetail.
*/
DeviceHandle=CreateFile \
(detailData->DevicePath, \
GENERIC_READ|GENERIC_WRITE, \
FILE_SHARE_READ|FILE_SHARE_WRITE, \
NULL, \
OPEN_EXISTING, \
0, \
NULL);
DisplayLastError("CreateFile: ");
/*
API function: HidD_GetAttributes
Requests information from the device.
Requires: the handle returned by CreateFile.
Returns: a HIDD_ATTRIBUTES structure containing
the Vendor ID, Product ID, and Product Version Number.
Use this information to decide if the detected device is
the one we're looking for.
*/
//Set the Size to the number of bytes in the structure.
Attributes.Size = sizeof(Attributes);
Result = HidD_GetAttributes \
(DeviceHandle, \
&Attributes);
DisplayLastError("HidD_GetAttributes: ");
//Is it the desired device?
MyDeviceDetected = FALSE;
if (Attributes.VendorID == VendorID)
{
if (Attributes.ProductID == ProductID)
{
//Both the Product and Vendor IDs match.
MyDeviceDetected = TRUE;
DisplayData("Device detected");
//Get the device's capablities.
GetDeviceCapabilities();
//Use this handle for writing reports.
WriteHandle=DeviceHandle;
//ReadFile is a blocking call,
//so get another handle for another thread for reading reports.
ReadHandle=CreateFile \
(detailData->DevicePath, \
GENERIC_READ|GENERIC_WRITE, \
FILE_SHARE_READ|FILE_SHARE_WRITE, \
NULL, \
OPEN_EXISTING, \
0, \
NULL);
DisplayLastError("CreateFile for Read Handle: ");
/*
Create a thread for reading reports from the device.
ReadFile is a blocking call, so it's called in a separate program thread.
This keeps the main program thread from hanging while waiting for a
report from the device.
In CreateThread, StaticIO_Thread is a static member that accepts
the "this" pointer and casts it to a pointer to the ReadReport routine,
which does the ReadFile.
*/
ThreadHandle = CreateThread \
(NULL, \
0, \
(LPTHREAD_START_ROUTINE)StaticIO_Thread, \
this, \
0, \
&ThreadID);
if (ThreadHandle == NULL)
CloseHandle(ThreadHandle);
DisplayLastError("CreateThread: ");
} //if (Attributes.ProductID == ProductID)
else
//The Product ID doesn't match.
CloseHandle(DeviceHandle);
} //if (Attributes.VendorID == VendorID)
else
//The Vendor ID doesn't match.
CloseHandle(DeviceHandle);
//Free the memory used by the detailData structure (no longer needed).
free(detailData);
} //if (Result != 0)
else
//SetupDiEnumDeviceInterfaces returned 0, so there are no more devices to check.
LastDevice=TRUE;
//If we haven't found the device yet, and haven't tried every available device,
//try the next one.
MemberIndex = MemberIndex + 1;
} //do
while ((LastDevice == FALSE) && (MyDeviceDetected == FALSE));
if (MyDeviceDetected == FALSE)
DisplayData("Device not detected");
SetupDiDestroyDeviceInfoList(hDevInfo);
DisplayLastError("SetupDiDestroyDeviceInfoList");
return MyDeviceDetected;
}
void CUsbhidiocDlg::GetDeviceCapabilities()
{
//Get the Capabilities structure for the device.
PHIDP_PREPARSED_DATA PreparsedData;
/*
API function: HidD_GetPreparsedData
Returns: a pointer to a buffer containing the information about the device's capabilities.
Requires: A handle returned by CreateFile.
There's no need to access the buffer directly,
but HidP_GetCaps and other API functions require a pointer to the buffer.
*/
HidD_GetPreparsedData \
(DeviceHandle, \
&PreparsedData);
DisplayLastError("HidD_GetPreparsedData: ");
/*
API function: HidP_GetCaps
Learn the device's capabilities.
For standard devices such as joysticks, you can find out the specific
capabilities of the device.
For a custom device, the software will probably know what the device is capable of,
and the call only verifies the information.
Requires: the pointer to the buffer returned by HidD_GetPreparsedData.
Returns: a Capabilities structure containing the information.
*/
HidP_GetCaps \
(PreparsedData, \
&Capabilities);
DisplayLastError("HidP_GetCaps: ");
//Display the capabilities
CString ValueToDisplay;
ValueToDisplay.Format("%s%X", "Usage Page: ", Capabilities.UsagePage);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Input Report Byte Length: ", Capabilities.InputReportByteLength);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Output Report Byte Length: ", Capabilities.OutputReportByteLength);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Feature Report Byte Length: ", Capabilities.FeatureReportByteLength);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Link Collection Nodes: ", Capabilities.NumberLinkCollectionNodes);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Input Button Caps: ", Capabilities.NumberInputButtonCaps);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of InputValue Caps: ", Capabilities.NumberInputValueCaps);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of InputData Indices: ", Capabilities.NumberInputDataIndices);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Output Button Caps: ", Capabilities.NumberOutputButtonCaps);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Output Value Caps: ", Capabilities.NumberOutputValueCaps);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Output Data Indices: ", Capabilities.NumberOutputDataIndices);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Feature Button Caps: ", Capabilities.NumberFeatureButtonCaps);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Feature Value Caps: ", Capabilities.NumberFeatureValueCaps);
DisplayData(ValueToDisplay);
ValueToDisplay.Format("%s%d", "Number of Feature Data Indices: ", Capabilities.NumberFeatureDataIndices);
DisplayData(ValueToDisplay);
//No need for PreparsedData any more, so free the memory it's using.
HidD_FreePreparsedData(PreparsedData);
DisplayLastError("HidD_FreePreparsedData: ") ;
}
void CUsbhidiocDlg::ReadAndWriteToDevice()
{
//If we haven't done so already, find the device and learn its capabilities.
//Then send a report and request a report.
//The test device firmware (usbhidio) adds 1 to each byte received in an OUT report
//and sends the results back in the next IN report.
//Clear the List Box (optional).
//m_ResultsList.ResetContent();
//Don't attempt anything if a previous transfer hasn't completed.
{
DisplayData("***HID Test Report***");
DisplayCurrentTime();
//If the device hasn't been detected already, look for it.
if (DeviceDetected==FALSE)
DeviceDetected=FindTheHID();
if (DeviceDetected==TRUE)
{
//Write a report to the device.
WriteReport();
//Get the latest report read from the device and display it.
DisplayInputReport();
} // (EndIf: if DeviceDetected == True)
}
}
DWORD CUsbhidiocDlg::StaticIO_Thread(LPVOID Param)
{
//Used in creating a thread for ReadFiles.
//Accepts the "this" pointer and casts it to a pointer to ReadReport.
if (Param != NULL)
return ((CUsbhidiocDlg*)Param)->ReadReport();
else
return -1;
}
DWORD WINAPI CUsbhidiocDlg::ReadReport()
{
CString ByteToDisplay = "";
ULONG InputReportLength = 0;
CString MessageToDisplay = "";
ULONG Result;
//Read a report from the device.
InputReportLength=Capabilities.InputReportByteLength;
do
{
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -