查看: 76872|回復: 324|關注: 0
打印 上一主題 下一主題

奇迹觉醒私服: 【視頻】C++ MFC界面讀寫USB HID設備數據程序

  [復制鏈接]

論壇優秀回答者

奇迹觉醒女神之光 www.mhotr.icu 1532

主題

1萬

帖子

482

最佳答案
QQ
  • 關注者: 5266
跳轉到指定樓層
1#
發表于 2011-2-24 00:37:14 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
【視頻】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)

Executable Files:



Include Files:



Reference Files:



Library files:



Source files:



點擊Solution Explorer,右鍵點擊項目名稱,調查屬性窗口,在linker的input下面,加上兩個lib文件,如下圖所示:

建立一個dialog MFC窗口程序,項目屬性設置如下:
math (博士、教授)Email: [email protected]     QQ: 1023785467
我在網絡上的言論、見解等只代表我個人的觀念,與任何研究機構、商業公司等無關?;隊閫ü魏畏絞接胛姨教盅鹺圖際跎系奈侍猓ㄑ崳實幕?,請在論壇上發帖提問)。最新日志: 專程去北京拜訪宋知用老師

論壇優秀回答者

1532

主題

1萬

帖子

482

最佳答案
QQ
  • 關注者: 5266
2#
 樓主| 發表于 2011-2-24 02:09:17 | 只看該作者
第一步:列舉所有的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里面!
math (博士、教授)Email: [email protected]     QQ: 1023785467
我在網絡上的言論、見解等只代表我個人的觀念,與任何研究機構、商業公司等無關?;隊閫ü魏畏絞接胛姨教盅鹺圖際跎系奈侍猓ㄑ崳實幕?,請在論壇上發帖提問)。最新日志: 專程去北京拜訪宋知用老師

論壇優秀回答者

1532

主題

1萬

帖子

482

最佳答案
QQ
  • 關注者: 5266
3#
 樓主| 發表于 2011-2-24 02:34:31 | 只看該作者
第二步:循環讀取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;i<p->m_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;
math (博士、教授)Email: [email protected]     QQ: 1023785467
我在網絡上的言論、見解等只代表我個人的觀念,與任何研究機構、商業公司等無關?;隊閫ü魏畏絞接胛姨教盅鹺圖際跎系奈侍猓ㄑ崳實幕?,請在論壇上發帖提問)。最新日志: 專程去北京拜訪宋知用老師

論壇優秀回答者

1532

主題

1萬

帖子

482

最佳答案
QQ
  • 關注者: 5266
4#
 樓主| 發表于 2011-2-24 02:34:55 | 只看該作者
第三步:向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.GetCount();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;
math (博士、教授)Email: [email protected]     QQ: 1023785467
我在網絡上的言論、見解等只代表我個人的觀念,與任何研究機構、商業公司等無關?;隊閫ü魏畏絞接胛姨教盅鹺圖際跎系奈侍猓ㄑ崳實幕?,請在論壇上發帖提問)。最新日志: 專程去北京拜訪宋知用老師

論壇優秀回答者

1532

主題

1萬

帖子

482

最佳答案
QQ
  • 關注者: 5266
5#
 樓主| 發表于 2011-2-24 02:44:10 | 只看該作者
到現在為止,你就一個比較完善的USB讀寫HID程序了!

祝大家使用愉快...
math (博士、教授)Email: [email protected]     QQ: 1023785467
我在網絡上的言論、見解等只代表我個人的觀念,與任何研究機構、商業公司等無關?;隊閫ü魏畏絞接胛姨教盅鹺圖際跎系奈侍猓ㄑ崳實幕?,請在論壇上發帖提問)。最新日志: 專程去北京拜訪宋知用老師

新手

10 麥片

財富積分


050


0

主題

8

帖子

0

最佳答案
6#
發表于 2011-2-24 09:37:11 | 只看該作者
太感謝了

新手

5 麥片

財富積分


050


0

主題

130

帖子

0

最佳答案
7#
發表于 2011-3-1 16:10:54 | 只看該作者
感謝math大哥,回去試試看

新手

10 麥片

財富積分


050


1

主題

108

帖子

0

最佳答案
8#
發表于 2011-3-7 12:27:45 | 只看該作者
學習!thank you!

新手

5 麥片

財富積分


050


0

主題

193

帖子

0

最佳答案
QQ
9#
發表于 2011-3-7 23:38:20 | 只看該作者

新手

5 麥片

財富積分


050


0

主題

8

帖子

0

最佳答案
10#
發表于 2011-3-9 16:25:10 | 只看該作者
好東西,太感謝了
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規則

關閉

站長推薦上一條 /3 下一條

快速回復 奇迹觉醒女神之光 返回列表