hidsdi,hidpi.h,hidusage.h,hid.lib,setupapi.lib拷貝進工程檔案夾下,接下來調用API函數,完成需求。
extern "C" {
// Declare the C libraries used
#include "hidsdi.h" // Must link in hid.lib
#include <setupapi.h> // Must link in setupapi.lib
}
本文介紹Visual C++6.0環境下利用Windows API函數實作與HID裝置類的USB接口通信,并給出了通信程式的部分代碼
Windows下,與USB外設的任何通信需通過裝置驅動,該驅動知道如何與系統的USB驅動和通路裝置的應用程式通信,Windows
包含應用程式與HID通信所需要的各種資訊,不需要再安裝裝置驅動。Win32的應用程式接口API函數,使得裝置驅動能與應用程式之間互相通信,應用程式也不需要為了和USB裝置通信去了解複雜的USB協定。
下面用Visual C++編寫應用程式調用API函數,進而簡化了與硬體通信的過程。
1、查找USB裝置
在應用程式能與HID交換資料之前,它先要找到裝置,擷取關于它的報告資訊。
1)HidD_GetHidGuid(&guidHID);來獲得HID裝置的辨別,HID類裝置是通過GUID類型值作辨別的。GUID是16位元組大小的結構,用來辨別通信接口及類對象,它的定義為:
typedef struct _GUID // size is 16
{
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} GUID;
2)還調用了其他與硬體相關的API函數,這些函數都在setupapi.h中定義。
SetupDiGetClassDevs函數用來獲得一類硬體裝置的資訊,裝置資訊集句柄hDevInfo
HDEVINFO hDevInfo=SetupDiGetClassDevs(
&guidHID, //這類設别配置或接口類GUID
NULL, //特定的字元串,用來選擇符合條件的裝置
0, //與獲得資訊相關的頂層窗體句柄
DIGCF_PRESENT|DIGCF_DEVICEINTERFACE //給出了設定資訊的方式
);
3)SetupDiEnumDeviceInterface函數得到裝置接口資訊反複調用得到所有裝置接口資訊strInterfaceData,若要找到特定裝置,可在循環語句内調用
該函數,直到找到預期裝置或函數傳回False值,
定義為:
Bool bSuccess=SetupDiEnumDeviceInterface(
hDevInfo, //感興趣的接口句柄
NULL, //指向SP_DEVINFO_DATA類型結構的指針,該結構定義了特定接口
&guidHID, //确定了接口的GUID辨別
Index, //所關心的索引号,以0為起點
&strInterfaceData, //指向SP_DEVICE_INTERFACE_DATA類型的指針,他所指向的内容就是調用函數的目的所在,當函數傳回時,strInterfaceData指向的結構就存在相關接口資訊
);
其中SP_DEVINFO_DATA結構定義為:
typedef struct SP_DEVINFO_DATA{
DWORD cbsize; //指定結構的大小
GUID calssGuid; //裝置的GUID辨別
DWORD DevInst; //用來通路裝置的句柄
ULONG_PTR Reserved;
}SP_DEVINFO_DATA,*PSP_DEVINFO_DATA;
SP_DEVICE_INTERFACE_DATA結構的定義如下:
typedef struct SP_DEVICE_INTERFACE_DATA{
DWORD cbsize; //是SP_DEVICE_INTERFACE_DATA結構的大小
GUID InterfaceClassGuid; //指定了接口的GUID辨別
DWORD Flags; //接口所處狀态
ULONG_PTR Reserved;
}SP_DEVICE_INTERFACE_DATA,*PSP_DEVICE_INTERFACE_DATA;
4)SetupDiGetDeviceInterfaceDetail()
long Result=SetupDiGetDeviceInterfaceDetail(
hDevInfo, //裝置資訊集句柄
&strInterfaceData, //裝置接口資訊
NULL, //裝置路徑
0, //輸出緩沖區大小
&Length,
NULL);
再次調用為了得到strInterfaceDetailData
long Result=SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&strInterfaceData,
strInterfaceDetailData,
Length,
&Required,
NULL
);
2、與USB裝置交換資料
在Windows中,讀寫端口與讀寫檔案都是調用同樣的API函數,打開或建立端口用CreateFile,從端口讀資料用ReadFile,用WriteFile向端口寫資料
1)裝置的打開與關閉
用API函數CreateFile來打開或建立裝置:
HANDLE hCom=CreateFile(
strInterfaceDetailData->DevicePath, //指定打開裝置名
GENERIC_READ|GENERIC_WRITE, //允許讀寫
0, //獨占方式
NULL, //安全模式
OPEN_EXISTING, //打開
FILE_ATTRIBUTE_NORNAL, //檔案屬性
NULL, //臨時檔案的句柄
);
如果調用成功,函數傳回檔案的句柄,如果調用失敗,則傳回INVALID_HANDLE_VALUE,在打開通信裝置時,應該以獨占方式打開,
不再使用裝置句柄時,應該調用CloseHandle(hCom)函數關閉它。
2)Bool Result = HidD_GetAttributes (hCom, &strAttrib);
其中hCom是對應于標明裝置的句柄,strAttribute則是指向HIDD_ATTRIBUTES類型的指針,當函數傳回時即得到了指定裝置的屬性
typedef struct _HIDD_ATTRIBUTES {
ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES)
USHORT VendorID; // Vendor ids of this hid device USHORT ProductID;
USHORT VersionNumber;
// Additional fields will be added to the end of this structure.
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
3)裝置的讀寫操作,讀寫通信裝置可用同步方式執行
HANDLE hCom;
void *pBuffer;
DWORD iLength;
DWORD pReadFact;
Bool ReadFile(hCom,pBuffer,iLength,&pReadFact,NULL);
讀到的資料放在記憶體pBuffer裡,pBuffer要先申請記憶體空間,iLength為需要讀的資料長度,pReadFact存放實際的資料長度。
需要注意的是在讀寫裝置之前,應先調用ClearCommError函數清除錯誤标志,此函數負責報告指定的錯誤的裝置的目前狀态,調用PrugeComm函數可以更改正在進行的讀寫操作方式
3、函數子產品執行個體
1)打開裝置
四個子產品中,打開裝置是最複雜的。因為它需要通過其裝置類來枚舉裝置樹上的所有裝置,進而得到相比對的裝置,進而得到裝置名
BOOL DeviceOpen(HANDLE&handle, WORD wVID, WORD wPID)
{
BOOL bRet = FALSE;
GUID hidGuid;
HDEVINFO hardwareDeviceInfo;
SP_INTERFACE_DEVICE_DATA deviceInfoData;
PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL;
ULONG predictedLength = 0;
ULONG requiredLength = 0;
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
deviceInfoData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
HidD_GetHidGuid(&hidGuid);
hardwareDeviceInfo = SetupDiGetClassDevs(&hidGuid, NULL,NULL, (DIGCF_PRESENT|DIGCF_DEVICEINTERFACE));
for (int i=0; i<128; i++)
{
if (!SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 0,&hidGuid, i, &deviceInfoData)) continue;
SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo, &deviceInfoData,NULL, 0, &requiredLength, NULL);
predictedLength = requiredLength;
functionClassDeviceData =(PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(predictedLength);
if (!functionClassDeviceData) continue;
functionClassDeviceData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail (hardwareDeviceInfo,&deviceInfoData, functionClassDeviceData, predictedLength,&requiredLength, NULL)) break;
handle = CreateFile(functionClassDeviceData->DevicePath,GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING, 0, NULL);
if (handle != INVALID_HANDLE_VALUE)
{
HIDD_ATTRIBUTES attri;
HidD_GetAttributes(handle, &attri);
if ((attri.VendorID == wVID) && (attri.ProductID == wPID))
{
bRet = TRUE;
break;
}
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
}
SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);
return bRet;
}
2)關閉裝置
關閉裝置比較簡單,隻需要直接使用函數CloseHandle即可
void DeviceClose(HANDLE&handle)
{
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
3)寫資料
假設HID的Report大小為8位元組,且第一位元組為ID
BOOL DeviceWrite(HANDLEhandle, LPCVOID lpBuffer, DWORD dwSize)
{
BYTE wBuffer[8] = {0};
DWORD dwRet;
BOOL bRet;
wBuffer[0] = 0x01;
wBuffer[1] = 0x00;
memcpy(&wBuffer[2], lpBuffer, min(6, dwSize));
bRet = WriteFile(handle, wBuffer, 8, &dwRet, NULL);
return bRet;
}
4)讀資料
BOOL DeviceRead(HANDLEhandle, LPVOID lpBuffer, DWORD dwSize)
{
BYTE rBuffer[8] = {0};
DWORD dwRet;
BOOL bRet;
rBuffer[0] = 0x01;
rBuffer[1] = 0xff;
bRet = WriteFile(handle, rBuffer, 8, &dwRet, NULL);
bRet &= ReadFile(handle, rBuffer, 8, &dwRet, NULL);
memcpy(lpBuffer, &rBuffer[1], min(7, dwSize));
return bRet;
}