在应用程序一个文档内引用头文件如下,并将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 );
在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; }
关闭设备比较简单,只需要直接使用函数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; }
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; }
引用链接:http://www.doc88.com/p-283792820785.html