第8章 USB接口HID设备 205
ByVal DeviceInfoData As Long, _
ByRef InterfaceClassGuid As GUID, _ ByVal MemberIndex As Long, _
ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA _ )As Long
? 结构定义
Public Type SP_DEVICE_INTERFACE_DATA cbSize As Long
InterfaceClassGuid As GUID Flags As Long Reserved As Long End Type
? 变量说明
Dim Result as Long
Dim MemberIndex as Long
Dim MyDeviceInterfaceData as SP_DEVICE_INTERFACE_DATA
? 调用
MyDeviceInterfaceData.cbSize = LenB(MyDeviceInterfaceData) MemberIndex = 0
Result = SetupDiEnumDeviceInterfaces( DeviceInfoSet, _ ?SetupDiGetClassDevs的返回值 0, _ HidGuid, _ ?通过HidD_GetHidGuid函数获得的GUID MemberIndex, _ ?第一次调用设置为0 MyDeviceInterfaceData _ )
参数cbSize是SP_DEVICE_INTERFACE_DATA结构的大小,以字节为单位。在调用SetupDiEnumDeviceInterfaces函数之前,cbSize必须储存在结构内来当做传递的参数。
参数HidGuid和DeviceInfoSet是函数之前的传回值。
DeviceInfoData是SP_DEVICE_INTERFACE_DATA结构的指针,用来限制检测特定设备的接口。MemberIndex是DeviceInfoSet数组的索引值,在遍历整个数组的循环中MemberIndex递增。MyDeviceInterfaceData是回传的结构,用来识别HID的一个接口。
(4) 获得设备路径
下面通过调用SetupDiGetDeviceInterfaceDetail函数用来获得另外一个结构:SP_DEVICE_INTERFACE_DETAIL_DATA。此结构与前一个函数SetupDiEnumDeviceInterfaces所识别的设备接口有关。结构的DevicePath成员是一个设备路径,应用程序可以用此路径来实现与该设备的通信。
? 函数声明: Public Declare Function SetupDiGetDeviceInterfaceDetail Lib \ Alias \ ByVal DeviceInfoSet As Long, _ ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA, _ ByVal DeviceInterfaceDetailData As Long, _ ByVal DeviceInterfaceDetailDataSize As Long, _ ByRef RequiredSize As Long, _ ByVal DeviceInfoData As Long _ ) As Long 206 计算机高级接口实践
? 结构声明
Public Type SP_DEVICE_INTERFACE_DETAIL_DATA cbSize As Long
DevicePath As Byte End Type
? 变量定义
Dim Needed as Long, DetailData as long
Dim MyDeviceInterfaceDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA Dim DetailDataBuffer() as Byte Dim DevicePathName As String
? 调用
MyDeviceInterfaceData.cbSize = LenB(MyDeviceInterfaceData)
Result = SetupDiGetDeviceInterfaceDetailA( DeviceInfoSet, _ DeviceInterfaceData, _ 0, _ 0, _ Needed, _ 0 _ )
DetailData = needed
MyDeviceInterfaceDetailData.cbSize = Len(MyDeviceInterfaceDetailData) ReDim DetailDataBuffer(Needed)
Call RtlMoveMemory(DetailDataBuffer(0), MyDeviceInterfaceDetailData, 4) Result = SetupDiGetDeviceInterfaceDetailA( DeviceInfoSet, _ DeviceInterfaceData, _ VarPtr(DetailDataBuffer(0)), _ DetailData, _ Needed, _ 0 _ )
? 转换成字符串,再转换成Unicode并从中删除4个字符得到设备路径 DevicePathName = Cstr(DetailDataBuffer())
DevicePathName = StrCovn(DevicePathName, vbUnicode)
DevicePathName = Right$(DevicePathName, Len(DevicePathName)-4)
DeviceInterfaceDetailDataSize包含DeviceInterfaceData结构的大小,以字节为单位。但是,在调用SetupDiGetDeviceInterfaceDetail函数之前无法知道此数值的大小,如果没有DeviceInterfaceDetailDataSize函数,SetupDiGetDeviceInterfaceDetail函数不会传回所需的结构。
这个问题的解决方式是两次调用SetupDiGetDeviceInterfaceDetail函数。第一次调用时GetLastError函数会传回错误信息,即:The data erea passed to a system call is too small,但是RequireSize参数会包含正确的DeviceInterfaceDetailDataSize数值。再次调用时传递此传回值,函数就会执行成功。
(5) 获得设备句柄
取得设备的路径以后,就可以准备开始与设备通信。使用CreateFile来打开一个HID设备,并且取得此设备的句柄,使用设备的句柄来与设备交换数据。当不再需要访问此设备时,应该调用CIoseHandle函数来关闭设备并释放系统资源。
第8章 USB接口HID设备 207
? 函数声明:
Public Declare Function CreateFile Lib \ ByVal lpFileName As String, _ ByVal dwDesiredAccess As Long, _ ByVal dwShareMode As Long, _
ByRef lpSecurityAttributes As Long, _ ByVal dwCreationDisposition As Long, _ ByVal dwFlagsAndAttributes As Long, _ ByVal hTemplateFile As Long _ ) As Long
? 常量定义
Public Const GENERIC_READ = &H80000000 Public Const GENERIC_WRITE = &H40000000 Public Const FILE_SHARE_READ = &H1 Public Const FILE_SHARE_WRITE = &H2 Public Const OPEN_EXISTING = 3
? 调用
HidDevice = CreateFile( _ DevicePathName, _
GENERIC_READ Or GENERIC_WRITE, _
(FILE_SHARE_READ Or FILE_SHARE_WRITE), _ 0, _
OPEN_EXISTING, _ 0, _ 0 )
(6) 获得设备的厂商ID、产品ID和产品序列号
要识别一个设备是否是所要的设备,只要比较此设备的厂商与产品ID 即可。
HidD_GetAttributes函数用来取得一个包含厂商与产品ID以及产品的版本号码的结构。
? 函数声明
Public Declare Function HidD_GetAttributes Lib \ByVal HidDeviceObject As Long, _
ByRef Attributes As HIDD_ATTRIBUTES _ ) As Long
? 结构说明
Public Type HIDD_ATTRIBUTES Size As Long
VendorID As Integer ?厂商ID ProductID As Integer ?产品ID VersionNumber As Integer ?产品版本号 End Type
? 变量定义
Dim DeviceAttributes As HIDD_ATTRIBUTES
? 调用
DeviceAttributes.Size = LenB(DeviceAttributes) Result = HidD_GetAttributes( _ HidDevice, _ ?由CreateFile函数返回 DeviceAttributes )
208 计算机高级接口实践 HidDevice是由 CreateFile函数所传回的设备句柄。如果CreateFile函数传回的是非零值,DeviceAttributes结构就会填写正确值。应用程序可以由DeviceAttributes结构取得厂商ID、产品ID以及产品的版本号码。
如果厂商与产品ID不是想查找的,应用程序应该使用CloseHandle函数来关闭该设备,然后移到下一个SetupDiEnumDeviceInterfaces所检测到的下一个HlD继续查找。当应用程序检测到指定的设备或是检测完全部HID,它应该调用
SetupDiDestroyDeviceInfoList函数来释放SetupDiGetClassDevs函数所保留的资源。
8.5.3 获得HID的能力
获得设备的能力是可以进一步了解HID的手段,但不是必须的。如果在查找设备时,如果通过厂商ID、产品ID和产品序列号可以确定特定的设备,一般是已知设备的细节信息了。如果不通过厂商ID、产品ID和产品序列号确定设备,另一个方法是通过获得设备能力来确定设备。
获得HID的能力的过程主要经过以下几个步骤:
? 先使用HidD_GetPreparsedData函数获得一个包含设备能力信息的缓冲区的指
针,调用HidP_GetCaps获得描述HID的能力的数据结构; ? 调用HidP_GetValueCaps取得描述能力的数值。 (1) 获得描述HID能力的数据结构
? 函数声明 Public Declare Function HidD_GetPreparsedData Lib \ ByVal HidDeviceObject As Long, _ ByRef PreparsedData As Long ) As Long ? 变量定义 Dim PreparsedData As Long ? 调用 Result = HidD_GetPreparsedData (HidDevice, _ ? 由CreateFile获得的设备句柄 PreparsedData _ ) PreparsedData是一个包含数据的缓冲区的指针。程序并不需要访问缓冲区内的数据,只需要将缓冲区的开始地址传递给其他的API函数。当应用程序不再需要PreparsedData时,它应该调用HidD_FreePreparsedData函数来释放系统资源。
接下来调用HidP_GetCaps,该函数传回一个结构,里面包含设备能力的信息,包括设备的Usage Page、Usage、报表长度以及按钮能力和数值能力等的数目。如果不使用厂商与产品ID来识另设备,HidP_GetCaps函数传回的设备能力信息可以帮助决定是否继续与此设备通信。
? 函数声明 Public Declare Function HidP_GetCaps Lib \ ByVal PreparsedData As Long, _ ByRef Capabilities As HIDP_CAPS ) As Long ? 结构定义 Public Type HIDP_CAPS