Windows下开发HID设备主机程序

转载请注明出处

作者:小马

本文章主要介绍一下如何在XP下做一个基于usb hid设备的上位机程序,实现简单的上位机与硬件设备的通信. 由于本人自身的能力限制,有不足和出错的地方,希望读者见谅.我假设这篇文章的读者已经对USB, HID,报告描述符等相关概念都至少有所了解,如果不是的话,自行学习.

开发环境, vs2005, DDK的支持.如果没有安装DDK,去网上找相关的库文件和头文件也行. 有以下几个文件是所需的:

basetsd.hhidclass.hhidpddi.hhidpi.hhidsdi.hhidusage.hhid.libhidclass.libhidparse.libsetupapi.lib开发这种程序并不复杂,起码跟用DDK自己写驱动比起来,简单很多, 主要是对DDK里的一些接口的功能要熟悉,这样才能用起来得心应手

一 识别设备

要和自己的HID设备通信,第一步当然是找到设备.原理很简单,我们把读到的设备信息与实际设备的信息相比较,就可以知道是否读到了正确的设备. 在USB设备中,设备描述符里的信息可以唯一的标识不同的USB设备. 我们一般用

idVendor idProduct bcdDevice这三个信息识别一个USB设备. 这三个信息都是设备描述符里的属性. 所以我们的上位机程序可以把读到的上述三个信息与实际设备的相比较,从而确定是否正确的连接到了设备. 实际设备的设备描述符在设备的固件程序中可以找得到,如果你没有固件程序的源码,也可以通过一些工具软件读出来设备的描述符信息,比如USB View就是一个很好用的工具.

知道了识别设备的原理,就可以通过DDK里相关的API接口去实现了.

HidD_GetAttributes函数可以获取到上面的属性信息, 它的定义如下:

BOOLEAN HidD_GetAttributes(IN HANDLE HidDeviceObject,OUT PHIDD_ATTRIBUTES Attributes);

第二个参数是一个指向

HIDD_ATTRIBUTES结构体的指针, 这个结构体的定义如下:

typedef struct _HIDD_ATTRIBUTES { ULONG Size; USHORT VendorID; USHORT ProductID; USHORT VersionNumber;} HIDD_ATTRIBUTES这个函数可以从设备中读到我们想要的信息. 但是,函数还有一个入口参数, HidDeviceObject,这是一个指向设备的句柄,所以在调用HidD_GetAttributes前,先要调用CreateFile函数返回一个有效的设备操作句柄. 有了这个句柄才能与设备进行正常的通信.

CreateFile的第一个参数要求提供一个设备名,这里我们要提供一个完整的设备路径名,否则将返回无效的句柄. 这个路径名是操作系统在识别到设备后分配给设备的, 可以通过DDK里的接口SetupDiGetDeviceInterfaceDetail来获取到, 这个函数的定义如下:

SetupDiGetDeviceInterfaceDetailW(__in HDEVINFO DeviceInfoSet,__in PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,__out_bcount_opt(DeviceInterfaceDetailDataSize) PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData,__in DWORD DeviceInterfaceDetailDataSize,__out_opt PDWORD RequiredSize,__out_opt PSP_DEVINFO_DATA DeviceInfoData);

该函数可以获取到一个设备接口的详细信息, 注意第三个参数, 我们要的那个路径名就由第三个参数返回. 它的结构体定义如下:

typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA_W {DWORD cbSize;WCHAR DevicePath[ANYSIZE_ARRAY];} SP_DEVICE_INTERFACE_DETAIL_DATA_W

第二个数据就是我们要的路径名.

这个函数的参数比较多,先来看一下第三个参数, 它用来接收设备的相关信息, 根据MSDN上的说明,我们可以这样定义:

PSP_DEVICE_INTERFACE_DETAIL_DATADetailDataBuffer;DetailDataBuffer = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize);DetailDataBuffer -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);第四个参数指明第三个参数的大小, 第五个参数是一个出口参数, 它由系统返回,告诉我们实际需要的空间大小. 所以我们可以调用两次SetupDiGetDeviceInterfaceDetail函数, 第一次获取RequiredSize的值, 然后把它当作第四个参数来用, 如下:

SetupDiGetDeviceInterfaceDetail (DeviceInfoSet, &MyDeviceInterfaceData, NULL, 0, &RequiredSize, NULL);SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,&MyDeviceInterfaceData, DetailDataBuffer, RequiredSize,&RequiredSize, NULL);

前两个参数都是入参,要获取它们的值,还需要调用其它的一些DDK 接口. 第二个参数MyDeviceInterfaceData 需要用SetupDiEnumDeviceInterfaces来获取到, 该函数的定义如下:

SetupDiEnumDeviceInterfaces(__in HDEVINFO DeviceInfoSet,__in_opt PSP_DEVINFO_DATA DeviceInfoData,__in CONST GUID *InterfaceClassGuid,__in DWORD MemberIndex,__out PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);

它的功能是可以枚举到某一类设备中,某一个设备的接口信息, InterfaceClassGuid指明设备的类别, 用GUID标识, 我的设备就是标准的HID设备, MemberIndex具体指示某一个设备, 比如我电脑上连接了两个HID设备,分别是鼠标和键盘,它们共用一个GUID,我用MemberIndex来区分它们,可能0对应鼠标,1对应键盘. 所以很明显,这个函数可以循环调用,通过改变MemberIndex的值(从0到n), 一直到找到我们所需的设备为止.

最后就是如何获取设备的GUID了, 要用一个函数,

HidD_GetHidGuid ( OUT LPGUID HidGuid );

这个函数传出一个GUID类型的数据, 得到的结果类似下面的形式:

GUID: {4D1E55B2-F16F-11CF-88CB-001111000030}

有了上面的函数已经可以正确的识别到一个USB HID的设备了, DDK里还提供了一些函数,可以在找到设备后, 获取设备更详细的信息,下面举几个比较常用的.

BOOLEAN __stdcall HidD_GetProductString( __in HANDLE HidDeviceObject, __out PVOID Buffer, __in ULONG BufferLength);这个函数可以获取到设备的产品字符串, 当然,前提是设备里要有产品字符串描述符, 因为字符串描述符在设备中是可选的. 该函数的第一个参数是CreateFile返回的句柄, 字符串描述符是用宽字符来表示的,所以读取时要注意转换. 同一类型的函数还有:

HidD_GetManufacturerStringHidD_GetSerialNumberString1HidD_GetIndexedString

另外,还有一个比较重要的函数

NTSTATUS HidP_GetCaps( PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);

这个函数可以获取设备的通信能力, 它的第二个出口参数是这样的一个结构体:

灿烂甜美!那一瞬的激-情绽放,催人奋进!胜利,永远属于为梦想奋斗的人新乐吧

Windows下开发HID设备主机程序

相关文章:

你感兴趣的文章:

标签云: