[轉]C++ MFC介面讀寫USB HID裝置資料程式
C++ MFC介面讀寫USB HID裝置資料程式
跟大家分享一個最近開發的,用來測試USB HID裝置的通用程式,程式還有很多可以繼續優化的地方,比如說interupt的週期,讀取位元組的多少等。
我遇到的問題是:開發一個簡單易用的介面,用來對USB HID裝置(比如說遊戲手柄,控制面板等)讀寫資料,一般情況下面板上有一些LED,可以幫助我們測試讀寫是否正確。另外,需要可以修改vendorID和prodcutID,這樣一個介面,可以用於測試多個HID裝置。
過程分成3步:1: 列舉出所有的HID裝置,2: 迴圈讀取HID裝置資料,3: 向HID裝置寫資料,下面我把三部分的程式單獨分開,方便大家學習!
在講具體程式之前,先說一下visual studio的環境配置(我用的是2008版本)!
首先建立一個MFC dialog程式,然後進行配置。
Tools->Options->VC++ Directories Platform (Win32)
第一步:列舉所有的HID裝置:
m_ctllHIDdevices.ResetContent(); //這是MFC裡面一個list控制元件,用來顯示所有的HID裝置的,如果你沒有介面,可以不需要此行
UpdateData(FALSE); //更新介面
CString temp;
int Count = 0; //Total number of devices found
DWORD strSize=0,requiredSize=0;
BOOL result1,result2;
ULONG DeviceInterfaceDetailDataSize;
//定義一些變數,以後會用到
SP_DEVINFO_DATA DeviceInfoData;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
//PSP_DEVICE_INTERFACE_DETAIL_DATA test;
//第一步:獲取deviceID
GUID deviceId;
HidD_GetHidGuid(&deviceId);
//第二步:獲取裝置資訊
HDEVINFO handle;
handle = SetupDiGetClassDevs(&deviceId, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); //Get only HID devices
//第三步:對所有的裝置進行列舉
//SetupDiEnumDeviceInterfaces();
result1=false; //定義一些變數
result2=false;
CString temp11="";
do
{
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
result1 = SetupDiEnumDeviceInterfaces(
handle,
NULL, // IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
&deviceId,
Count,
&DeviceInterfaceData
);
//獲得裝置詳細資料(初步)
SetupDiGetDeviceInterfaceDetail(handle,
&DeviceInterfaceData,
NULL,
0,
&strSize,
NULL);
requiredSize=strSize;
DeviceInterfaceDetailData=(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
DeviceInterfaceDetailData->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
DeviceInfoData.cbSize=sizeof(SP_DEVINFO_DATA);
//再次獲得詳細資料
result2=SetupDiGetDeviceInterfaceDetail(handle,
&DeviceInterfaceData,
DeviceInterfaceDetailData,
strSize,
&requiredSize,
&DeviceInfoData);
//獲得裝置路徑(最重要的部分)
temp=DeviceInterfaceDetailData->DevicePath;
UpdateData(FALSE);
m_ctllHIDdevices.AddString(temp);
Count++;
} while (result1);
UpdateData(false);
到這個時候為止,所有的裝置路徑,都會顯示在視窗的listbox裡面!
第二步:迴圈讀取HID裝置資料(根據使用者提供的HID的vendorID和productID),並且把位元組解碼成二進位制,在MFC介面上用LED展示:
為了不影響主執行緒的執行,我把讀取資料的操作,放在一個子執行緒裡!每隔50ms去讀取一次資料!
首先建立一個執行緒:
HANDLE hThread1;
bStopHID=false; //這個變數,以後用來停止執行緒
UpdateData(true); //更新介面,獲取變數
UpdateData(false);
hThread1 = CreateThread(NULL,0,Thread_Enable_Read,(LPVOID)this, NULL, NULL);
線上程的程式裡:
CusbhidDlg *p = ( CusbhidDlg *)pvParam; //獲取主視窗的指標,用來呼叫主視窗的變數和函式
p->UpdateData(true);
p->bStopHID=false;
CString temp;
CString DevicePath;
temp="";
int Count = 0; //Total number of devices found
DWORD strSize=0,requiredSize=0;
BOOL result1,result2;
ULONG DeviceInterfaceDetailDataSize;
SP_DEVINFO_DATA DeviceInfoData;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
//PSP_DEVICE_INTERFACE_DETAIL_DATA test;
//1
GUID deviceId;
HidD_GetHidGuid(&deviceId);
int venderID=p->v_eVendorID; //從窗口裡獲取使用者輸入的VendorID
int productID=p->v_eProductID;//從窗口裡獲取使用者輸入的ProductID
unsigned char inbuffer[2]; //用來存放讀取的資料,請在這裡定義你自己需要的長度,我每次讀一個位元組進來
unsigned long numBytesReturned;
HIDD_ATTRIBUTES devAttr;
PHIDP_PREPARSED_DATA PreparsedData;
HIDP_CAPS Capabilities;
int readValue;
bool LED;
int flag=0;
//2
HDEVINFO handle;
handle = SetupDiGetClassDevs(&deviceId, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); //Get only HID devices
int i=0;
int j=p->m_ctllHIDdevices.GetCount();
for (i=0;im_ctllHIDdevices.GetCount();i++)
{
p->m_ctllHIDdevices.GetText(i,temp);
DevicePath=temp;
//CreateFile是非常重要的一步,用來建立於HID通訊的控制代碼
HANDLE hCom = CreateFile (
DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING, 0,
NULL);
if (hCom == INVALID_HANDLE_VALUE)
{
//AfxMessageBox("Invalide Device Path...");
continue;
}
devAttr.Size=sizeof(HIDD_ATTRIBUTES);
if (!HidD_GetAttributes(hCom,&devAttr))
{
CloseHandle(hCom);
AfxMessageBox("Cannot get the parameters of the HID...");
return 0;
}
//temp.Format("Vendor ID: %d, Product ID:%d",devAttr.VendorID,devAttr.ProductID); //Compare with the Vendor ID and Product ID from Nakamura-san
//AfxMessageBox(temp);
if (!HidD_GetPreparsedData(hCom,&PreparsedData))
{
CloseHandle(hCom);
AfxMessageBox("Cannot get the Preparsed Data...");
return 0;
}
if(!HidP_GetCaps(PreparsedData,&Capabilities))
{
CloseHandle(hCom);
AfxMessageBox("Cannot get the Cap Data...");
return 0;
}
if (devAttr.VendorID == venderID && devAttr.ProductID == productID)
{
while(1)
{
result1 = ReadFile(hCom, &inbuffer[0], Capabilities.InputReportByteLength, &numBytesReturned, 0);
temp=inbuffer;
//p->m_eDataRead=CString(inbuffer);
//p->UpdateData(false);
if(!result1)
{
AfxMessageBox("Cannot Read Data...");
return 0;
}
readValue=inbuffer[1];
p->m_eDataRead.Format("%d",readValue);
//下面是我把資料從10進位制轉換成二進位制,並且點亮LED (一個位元組有8個bits,可以點亮8個LED
for (int k=0;k<8;k++)
{
flag=readValue%2;
readValue=readValue/2;
if (k==0)
{
if (flag==0)
p->m_sDynLED0.SwitchOff();
else
p->m_sDynLED0.SwitchOn();
}
else if (k==1)
{
if (flag==0)
p->m_sDynLED1.SwitchOff();
else
p->m_sDynLED1.SwitchOn();
}
else if (k==2)
{
if (flag==0)
p->m_sDynLED2.SwitchOff();
else
p->m_sDynLED2.SwitchOn();
}
else if (k==3)
{
if (flag==0)
p->m_sDynLED3.SwitchOff();
else
p->m_sDynLED3.SwitchOn();
}
else if (k==4)
{
if (flag==0)
p->m_sDynLED4.SwitchOff();
else
p->m_sDynLED4.SwitchOn();
}
else if (k==5)
{
if (flag==0)
p->m_sDynLED5.SwitchOff();
else
p->m_sDynLED5.SwitchOn();
}
else if (k==6)
{
if (flag==0)
p->m_sDynLED6.SwitchOff();
else
p->m_sDynLED6.SwitchOn();
}
else if (k==7)
{
if (flag==0)
p->m_sDynLED7.SwitchOff();
else
p->m_sDynLED7.SwitchOn();
}
}
p->UpdateData(false);
::Sleep(50);
//判斷使用者是否點選停止按鈕,若是,則退出
if(p->bStopHID)
{
AfxMessageBox("stopped...");
return 0;
}
}
}
}
if (i==j)
{
AfxMessageBox("There is no such HID device...");
}
return 0;
第三步:向HID裝置寫資料(根據使用者提供的HID的vendorID和productID),使用者輸入的是二進位制資料:
與讀的程式一樣,唯一區別就是紅色那部分!
UpdateData(true);
bStopHID=false;
CString temp;
CString DevicePath;
temp="";
int Count = 0; //Total number of devices found
DWORD strSize=0,requiredSize=0;
BOOL result1,result2;
ULONG DeviceInterfaceDetailDataSize;
SP_DEVINFO_DATA DeviceInfoData;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
//PSP_DEVICE_INTERFACE_DETAIL_DATA test;
//1
GUID deviceId;
HidD_GetHidGuid(&deviceId);
int venderID=v_eVendorID;
int productID=v_eProductID;
unsigned char inbuffer[2];
unsigned long numBytesReturned;
HIDD_ATTRIBUTES devAttr;
PHIDP_PREPARSED_DATA PreparsedData;
HIDP_CAPS Capabilities;
int readValue;
bool LED;
int flag=0;
inbuffer[0]=0;
//把介面裡的二進位制轉換成10進位制
inbuffer[1]=m_eByte0*1+m_eByte1*2+m_eByte2*4+m_eByte3*8+m_eByte4*16+m_eByte5*32+m_eByte6*64+m_eByte7*128;
v_eDataToWrite=inbuffer[1];
UpdateData(false);
//2
HDEVINFO handle;
handle = SetupDiGetClassDevs(&deviceId, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); //Get only HID devices
int i=0;
int j=m_ctllHIDdevices.GetCount();
for (i=0;i
{
m_ctllHIDdevices.GetText(i,temp);
DevicePath=temp;
HANDLE hCom = CreateFile (
DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING, 0,
NULL);
if (hCom == INVALID_HANDLE_VALUE)
{
//AfxMessageBox("Invalide Device Path...");
continue;
}
devAttr.Size=sizeof(HIDD_ATTRIBUTES);
if (!HidD_GetAttributes(hCom,&devAttr))
{
CloseHandle(hCom);
AfxMessageBox("Cannot get the parameters of the HID...");
return;
}
//temp.Format("Vendor ID: %d, Product ID:%d",devAttr.VendorID,devAttr.ProductID); //Compare with the Vendor ID and Product ID from Nakamura-san
//AfxMessageBox(temp);
if (!HidD_GetPreparsedData(hCom,&PreparsedData))
{
CloseHandle(hCom);
AfxMessageBox("Cannot get the Preparsed Data...");
return;
}
if(!HidP_GetCaps(PreparsedData,&Capabilities))
{
CloseHandle(hCom);
AfxMessageBox("Cannot get the Cap Data...");
return;
}
// Write File
if (devAttr.VendorID == venderID && devAttr.ProductID == productID)
{
result1 = WriteFile(hCom, inbuffer, 2, &numBytesReturned, NULL);
//temp=inbuffer;
//p->m_eDataRead=CString(inbuffer);
//p->UpdateData(false);
if(!result1)
{
AfxMessageBox("Cannot Write Data...");
return;
}
AfxMessageBox("Suncess...");
break;
}
}
if (i==j)
{
AfxMessageBox("There is no such HID device...");
}
return;