Windows上HID设备上位机程序总结

来源:互联网 发布:sqlserver导出mdf 编辑:程序博客网 时间:2024/06/02 17:30

    Windows上HID设备上位机程序总结

1.      建立工程

Ø  Virtual Studio上建立“win32控制台”工程。

Ø  下载hid设备需要用到的头文件和lib文件到新建工程目录.

        文件包括:Hid.h , hidsdi.h setupapi.h, hid.lib  。至于setupapi.lib好像不需要。

Ø  修改工程属性,添加lib:链接器->输入->附加依赖项中添加:hid.lib   setupapi.lib

Ø  在新建工程xxx.cpp中添加头文件:

 

#include <windows.h>

#include <WinIoCtl.h>

#include <stdio.h>

 

extern "C"

{

#include "Hid.h"

#include "hidsdi.h"

#include "setupapi.h"

}

 

2.      识别设备:

1)        原理

调用DDK提供的接口函数,获得设备设备信息,其中VID,PID, bcdDevice可以用来识别唯一的USB设备。使用此信息与要通信的设备的信息比对是否一致。

下面从原理的流程上讲述调用的API。

a)        获取设备PID/DID等信息

设备信息的API定义如下:

BOOLEAN __stdcall

HidD_GetAttributes (

    IN  HANDLE              HidDeviceObject,

    OUT PHIDD_ATTRIBUTES    Attributes

    );

 

第二个参数就是设备属性结构,由API输出,此结构定义如下:

typedef struct _HIDD_ATTRIBUTES {

    ULONG   Size; // = sizeof (struct _HIDD_ATTRIBUTES)

 

    USHORT  VendorID;

    USHORT  ProductID;

    USHORT  VersionNumber;

 

} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;

要使用此函数,还需要第一个入口参数:HidDeviceObject,这是一个指向设备的句柄,所以在调用HidD_GetAttributes前,先要调用CreateFile函数返回一个有效的设备操作句柄. 有了这个句柄才能与设备进行正常的通信。

 

b)       CreateFile函数

此函数第一个参数需要完整的设备路径名,这个路径名是操作系统在识别到设备后分配给设备的, 可以通过DDK里的接口SetupDiGetDeviceInterfaceDetail来获取。

 

c)        SetupDiGetDeviceInterfaceDetail函数

此函数定义如下:

BOOL SetupDiGetDeviceInterfaceDetail(

  _In_       HDEVINFO DeviceInfoSet,

  _In_       PSP_DEVICE_INTERFACE_DATADeviceInterfaceData,

  _Out_opt_  PSP_DEVICE_INTERFACE_DETAIL_DATADeviceInterfaceDetailData,

  _In_       DWORD DeviceInterfaceDetailDataSize,

  _Out_opt_  PDWORD RequiredSize,

  _Out_opt_  PSP_DEVINFO_DATA DeviceInfoData

);

 

此函数可以获得接口设备详细信息,由第三个参数输出,第三个参数结构体定义如下:

typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {

  DWORD cbSize;

  TCHARDevicePath[ANYSIZE_ARRAY];

} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;

在此结构体中我们看见了CreateFile所需要的一个参数设备路径。

那我们该如何调用SetupDiGetDeviceInterfaceDetail函数呢?下面直接给出调用方法,具体参数含义等参考msdn。此处调用2遍,第一遍调用的第五个输出参数RequiredSize作为第二遍调用时第四个输入参数使用。

 

         HANDLE                                                                    hDevInfo;

         SP_DEVICE_INTERFACE_DATA                                              devInterfaceData;

         PSP_DEVICE_INTERFACE_DETAIL_DATA                                     detailData;

         SP_DEVINFO_DATA                                                         devInfoData;

 

//省略部分

 

ULONG     ulLength;

SP_DEVINFO_DATA  devInfoData;

BOOL        bResult;

bResult = SetupDiGetDeviceInterfaceDetail (

                            hDevInfo,//--------------------------------------

                            &devInterfaceData,//--------------------------------------

                            NULL,

                            0,

                            &ulLength,

                            &devInfoData);

detailData = NULL;

//Allocate memory for the hDevInfo structure, using the returnedLength.

detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) new unsignedchar[ulLength];

 

//Set cbSize in the detailData structure.

detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

 

//Call the function again, this time passing it the returned buffersize.

ULONG     ulRequired;

bResult = SetupDiGetDeviceInterfaceDetail(

                            hDevInfo,

                            &devInterfaceData,

                            detailData,

                            ulLength,

                            &ulRequired,

                            &devInfoData);

注:上面“省略部分”的程序完成功能是:准备SetupDiGetDeviceInterfaceDetail函数需要的2个输入参数。第一个输入参数DeviceInfoSet最后再说,先说第二个输入参数:DeviceInterfaceData,需要调用SetupDiEnumDeviceInterfaces函数获得(最后一个输出参数)。

 

d)       SetupDiEnumDeviceInterfaces函数

函数定义如下:

BOOL SetupDiEnumDeviceInterfaces(

  _In_      HDEVINFO DeviceInfoSet,

  _In_opt_  PSP_DEVINFO_DATA DeviceInfoData,

  _In_      const GUID *InterfaceClassGuid,

  _In_      DWORD MemberIndex,

  _Out_     PSP_DEVICE_INTERFACE_DATADeviceInterfaceData

);

此函数功能是枚举设备信息(DeviceInfoSet)中包含的某一个接口的信息(DeviceInterfaceData)。由于hid类下又可能有几个接口设备,比如鼠标,键盘,自己定义的HID设备等,MemberIndex就是索引依次获取设备信息。

第一个输入参数就是设备信息DeviceInfoSet(同SetupDiGetDeviceInterfaceDetail函数的第一个输入参数,最后讲述从何处获得),第二个参数可以为0,第三个参数是接口类型InterfaceClassGuid(下一步讲述),第四个参数就是索引,(所以此函数需要循环调用,index从0开始,直到获得PID,VID一致的设备,或者调用此函数返回FALSE),最有一个输出参数是设备接口数据DeviceInterfaceData,是调用此函数的目的,上面已讲述。

 

e)        其余参数的获得

这部分较为简单,不再赘述。代码如下:

GUID        m_Dev_Guid;  //SetupDiEnumDeviceInterfaces函数第三个参数

HANDLE   hDevInfo;   // SetupDiEnumDeviceInterfaces函数第一个参数,SetupDiGetDeviceInterfaceDetailW

 

 

HidD_GetHidGuid(&m_Dev_Guid);

hDevInfo=SetupDiGetClassDevs

                   (&m_Dev_Guid,

                   NULL,

                   NULL,

                   DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);

 

         devInterfaceData.cbSize =sizeof(devInterfaceData);

2)        识别过程具体代码

char m_szDevicePath[MAX_PATH];

 

BOOL m_bDevFound = FALSE;

BOOL m_bDevOpened = FALSE;

 

void ScanDeviceByVidPid(unsigned short usVendorID,

                                                                                              unsigned short usProductID)

{

         BOOL        bResult;  

         ULONG     ulLength;

         ULONG     ulRequired;

         int              iMemberIndex;

 

         GUID        m_Dev_Guid;

 

         //Use a series of APIcalls to find a HID with a specified Vendor IF and Product ID.

         HANDLE                                                                    hDevInfo;

         SP_DEVICE_INTERFACE_DATA                           devInterfaceData;

         PSP_DEVICE_INTERFACE_DETAIL_DATA         detailData;

         SP_DEVINFO_DATA                                                devInfoData;

 

         m_bDevFound = FALSE;

 

         HidD_GetHidGuid(&m_Dev_Guid);

        

         hDevInfo=SetupDiGetClassDevs

                   (&m_Dev_Guid,

                   NULL,

                   NULL,

                   DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);

 

         devInterfaceData.cbSize =sizeof(devInterfaceData);

 

         //Step through theavailable devices looking for the devices we want.

         //All desired deviceswill be added to list.

         for(iMemberIndex=0; ;iMemberIndex++)

         {

                   if(SetupDiEnumDeviceInterfaces (

                            hDevInfo,

                            0,

                            &m_Dev_Guid,

                            iMemberIndex,

                            &devInterfaceData)== FALSE)

                            break;

 

                   //A device hasbeen detected, so get more information about it.

                   ZeroMemory(&devInfoData,sizeof(devInfoData));

                   devInfoData.cbSize= sizeof(devInfoData);

 

                   //Get theLength value.

                   //The call willreturn with a "buffer too small" error which can be ignored.

                   ulLength = 0;

 

                   bResult =SetupDiGetDeviceInterfaceDetail (

                            hDevInfo,

                            &devInterfaceData,

                            NULL,

                            0,

                            &ulLength,

                            &devInfoData);

 

                   detailData =NULL;

                   //Allocatememory for the hDevInfo structure, using the returned Length.

                   detailData =(PSP_DEVICE_INTERFACE_DETAIL_DATA) new unsigned char[ulLength];

 

                   //Set cbSize inthe detailData structure.

                   detailData->cbSize= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

 

                   //Call thefunction again, this time passing it the returned buffer size.

                   bResult =SetupDiGetDeviceInterfaceDetail

                            (hDevInfo,

                            &devInterfaceData,

                            detailData,

                            ulLength,

                            &ulRequired,

                            &devInfoData);

 

 

                  // here we can use HidD_GetAttributes to read vid and pid.

                   // but thisinterface need devhandle which comes from createfile.

                   // so to avoidto create file many times, we just search device path for vid and pid.    

 

                   HANDLE hHandle=CreateFile(detailData->DevicePath,

                                                                                   GENERIC_READ|GENERIC_WRITE,

                                                                                   FILE_SHARE_READ|FILE_SHARE_WRITE,

                                                                                   (LPSECURITY_ATTRIBUTES)NULL,

                                                                                   OPEN_EXISTING,

                                                                                   NULL,

                                                                                   NULL);

 

                   BOOLEAN Result;

                   HIDD_ATTRIBUTESAttributes;

                   // Set the Sizemember to the number of bytes

                   // in thestructure.

                   Attributes.Size= sizeof(Attributes);

                   Result =HidD_GetAttributes(hHandle, &Attributes);

 

                   ::CloseHandle(hHandle);

                  

                   strcpy(m_szDevicePath,detailData->DevicePath);

                  

                   //Free thememory used by the detailData structure (no longer needed).

                   delete[]detailData;

 

 

                   if (Attributes.VendorID == usVendorID

                            &&Attributes.ProductID == usProductID )

                   {

                            m_bDevFound= TRUE;

                            break;

                   }

                   else

                   {

                            continue;

                   }

         }

 

         //Free the memoryreserved for hDevInfo by SetupDiClassDevs.

         SetupDiDestroyDeviceInfoList(hDevInfo);

}

3.      打开设备

 未完待续....

原创粉丝点击