USB学习笔记一《查找HID设备+示例代码》

一、USB设备HID
HID类是USB一个设备类型,微软的系统具有HID类的底层驱动,不用去编写驱动程序,只关心上层用户程序编写,通信协议即可。
需求:
1、C++编译器,如:VC6.0、VS2010;
2、
DDK的支持,或者网上下载库文件,头文件和源文件;
3、需要的添加的文件:
  1. basetsd.h  
  2. hidclass.h  
  3. hidpddi.h  
  4. hidpi.h  
  5. hidsdi.h  
  6. hidusage.h  
  7. hid.lib  
  8. hidclass.lib  
  9. hidparse.lib  
  10. setupapi.lib 
 二、HID设备识别
下位机需要自己编程,编写一些配置信息,把自己配置成HID设备;上位机要想从众多HID设备中查找到自己的HID,需要以下信息:
  1. idVendor   //厂商ID
  2. idProduct  //设备ID 
  3. bcdDevice  //软件版本号,可有可无
这三个信息识别一个USB设备  , HidD_GetAttributes函数可以获取到上面的属性信息,它的定义如下:
  1. BOOLEAN  
  2.   HidD_GetAttributes(  
  3.     IN HANDLE  HidDeviceObject,  
  4.     OUT PHIDD_ATTRIBUTES  Attributes  
  5.     );
  第二个参数是一个指向

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

  1. typedef struct _HIDD_ATTRIBUTES {  
  2.   ULONG  Size;  
  3.   USHORT  VendorID;  
  4.   USHORT  ProductID;  
  5.   USHORT  VersionNumber;  
  6. } HIDD_ATTRIBUTES 
这个函数可以从设备中读到我们想要的信息. 但是,函数还有一个入口参数, HidDeviceObject,这是一个指向设备的句柄, 所以在调用HidD_GetAttributes前,先要调用CreateFile函数返回一个有效的设备操作句柄. 有了这个句柄才能与设备进行正常的通信.   
hidHandle = CreateFile(devDetail->DevicePath,//要打开的文件的名或设备名
GENERIC_READ | GENERIC_WRITE,  //如果为 GENERIC_READ 表示允许对设备进行读访问;
 //如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);
 //如果为零,表示只允许获取与一个设备有关的信息
FILE_SHARE_READ | FILE_SHARE_WRITE,  //如果是FILE_SHARE_READ随后打开操作对象会成功只有请求读访问;
 //如果是FILE_SHARE_WRITE 随后打开操作对象会成功只有请求写访问
NULL,  //定义了文件的安全特性
OPEN_EXISTING,  //文件必须已经存在,由设备提出要求
FILE_FLAG_OVERLAPPED,  //允许对文件进行重叠操作
NULL);  //用于复制文件句柄
打开成功,则返回句柄,否则返回INVALID_HANDLE_VALUE。如果通过 GetLastError()函数,返回5,可以把第二个参数改为0。
CreateFile的第一个参数要求提供一个设备名,这里我们要提供一个完整的设备路径名,否则将返回无效的句柄. 这个路径名是操作系统在识别到设备后分配给设备的, 可以通过DDK里的接口SetupDiGetDeviceInterfaceDetail来获取到, 这个函数的定义如下:  
  1. SetupDiGetDeviceInterfaceDetailW(  
  2.     __in HDEVINFO DeviceInfoSet,  
  3.     __in PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,  
  4.     __out_bcount_opt(DeviceInterfaceDetailDataSize) PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData,  
  5.     __in DWORD DeviceInterfaceDetailDataSize,  
  6.     __out_opt PDWORD RequiredSize,   
  7.     __out_opt PSP_DEVINFO_DATA DeviceInfoData  
  8. ); 
  该函数可以获取到一个设备接口的详细信息, 注意第三个参数, 我们要的那个路径名就由第三个参数返回. 它的结构体定义如下:
  1. typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA_W {  
  2.     DWORD  cbSize;  
  3.     WCHAR  DevicePath[ANYSIZE_ARRAY];  
  4. } SP_DEVICE_INTERFACE_DETAIL_DATA_W
第二个数据就是我们要的路径名, 这个函数的参数比较多,先来看一下第三个参数, 它用来接收设备的相关信息, 根据MSDN上的说明,我们可以这样定义  :
  1. PSP_DEVICE_INTERFACE_DETAIL_DATA    DetailDataBuffer;  
  2. DetailDataBuffer = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize);  
  3. DetailDataBuffer -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 
第四个参数指明第三个参数的大小, 第五个参数是一个出口参数, 它由系统返回,告诉我们实际需要的空间大小. 所以我们可以调用两次SetupDiGetDeviceInterfaceDetail函数, 第一次获取RequiredSize的值, 然后把它当作第四个参数来用, 如下:  
  1. SetupDiGetDeviceInterfaceDetail  (DeviceInfoSet, &MyDeviceInterfaceData,   
  2. NULL, 0, &RequiredSize, NULL);  
  3.   
  4. SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,&MyDeviceInterfaceData,   
  5. DetailDataBuffer, RequiredSize,&RequiredSize, NULL);
前两个参数都是入参,要获取它们的值,还需要调用其它的一些DDK 接口. 第二个参数MyDeviceInterfaceData 需要用SetupDiEnumDeviceInterfaces来获取到, 该函数的定义如下:
  1. SetupDiEnumDeviceInterfaces(  
  2.     __in HDEVINFO DeviceInfoSet,  
  3.     __in_opt PSP_DEVINFO_DATA DeviceInfoData,  
  4.     __in CONST GUID *InterfaceClassGuid,  
  5.     __in DWORD MemberIndex,  
  6.     __out PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData  
  7.     ); 
它的功能是可以枚举到某一类设备中,某一个设备的接口信息, InterfaceClassGuid指明设备的类别, 用GUID标识, 我的设备就是标准的HID设备, MemberIndex具体指示某一个设备, 比如我电脑上连接了两个HID设备,分别是鼠标和键盘,它们共用一个GUID,我用MemberIndex来区分它们,可能0对应鼠标,1对应键盘. 所以很明显,这个函数可以循环调用,通过改变MemberIndex的值(从0到n), 一直到找到我们所需的设备为止.   
第一个参数可有SetupDiGetClassDevs函数得到,第三个参数可有
HidD_GetHidGuid得到。
HDEVINFO hDevInfo;
hDevInfo = SetupDiGetClassDevs(&hidGuid,NULL,NULL,(DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); //得到一类设备信息,只返回当前存在的设备,第一个参数需要 GUID  ,由   HidD_GetHidGuid得到。  
最后就是如何获取设备的GUID了, 要用一个函数:
  1. HidD_GetHidGuid (  
  2.   OUT   LPGUID   HidGuid  
  3.   ); 
  HidD_GetHidGuid(&hidGuid);//得到HID路径

这样,就可以检索到自己的HID设备。

代码示例:

HANDLE CDownLoadDlg::OpenMyHIDDevice(int overlapped)
{
// HANDLE hidHandle;       
GUID hidGuid;
HDEVINFO hDevInfo;
HidD_GetHidGuid(&hidGuid);//得到HID路径
hDevInfo = SetupDiGetClassDevs(&hidGuid,NULL,NULL,(DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); //得到一类设备信息,只返回当前存在的设备
if (hDevInfo == INVALID_HANDLE_VALUE)       
{           
return INVALID_HANDLE_VALUE;
}       
SP_DEVICE_INTERFACE_DATA devInfoData;       
devInfoData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);       
int deviceNo = 0; //设备列表中的序号


SetLastError(NO_ERROR);       
while (GetLastError() != ERROR_NO_MORE_ITEMS)
{           
if (SetupDiEnumInterfaceDevice (hDevInfo,0,&hidGuid,deviceNo,&devInfoData))//函数功能是从已经获取的设备接口列表信息中获取信息并使用结构保存,每调用一次会依次返回一个接口信息
{               
ULONG  requiredLength = 0;
//取得该设备接口的细节(设备路径)
SetupDiGetInterfaceDeviceDetail(hDevInfo,// 设备信息集句柄
&devInfoData, // 设备接口信息
NULL, // 设备接口细节(设备路径)
0, // 输出缓冲区大小
&requiredLength,// 计算输出缓冲区大小
NULL); // 不需额外的设备描述


// PSP_INTERFACE_DEVICE_DETAIL_DATA devDetail = (SP_INTERFACE_DEVICE_DETAIL_DATA*)malloc(requiredLength);//分配大小为requiredLength的内存块
PSP_INTERFACE_DEVICE_DETAIL_DATA devDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(requiredLength);//分配大小为requiredLength的内存块
devDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);


if(!SetupDiGetInterfaceDeviceDetail(hDevInfo,
&devInfoData,
devDetail,
requiredLength,
&requiredLength,
NULL))
{                   
free(devDetail);                   
SetupDiDestroyDeviceInfoList(hDevInfo);                   
return INVALID_HANDLE_VALUE;               
}




if (overlapped)               
{                   
hidHandle = CreateFile(devDetail->DevicePath,//要打开的文件的名或设备名
GENERIC_READ | GENERIC_WRITE,//如果为 GENERIC_READ 表示允许对设备进行读访问;
//如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);
//如果为零,表示只允许获取与一个设备有关的信息
// 0,
FILE_SHARE_READ | FILE_SHARE_WRITE,//如果是FILE_SHARE_READ随后打开操作对象会成功只有请求读访问;
//如果是FILE_SHARE_WRITE 随后打开操作对象会成功只有请求写访问
NULL, //定义了文件的安全特性
OPEN_EXISTING,//文件必须已经存在,由设备提出要求
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//允许对文件进行重叠操作
NULL); //用于复制文件句柄
if(hidHandle==INVALID_HANDLE_VALUE)
{
hidHandle = CreateFile(devDetail->DevicePath,//要打开的文件的名或设备名
// GENERIC_READ | GENERIC_WRITE,//如果为 GENERIC_READ 表示允许对设备进行读访问;
//如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);
//如果为零,表示只允许获取与一个设备有关的信息
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,//如果是FILE_SHARE_READ随后打开操作对象会成功只有请求读访问;
//如果是FILE_SHARE_WRITE 随后打开操作对象会成功只有请求写访问
NULL, //定义了文件的安全特性
OPEN_EXISTING,//文件必须已经存在,由设备提出要求
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//允许对文件进行重叠操作
NULL);
}


}               
else             
{                   
hidHandle = CreateFile(devDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
// 0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0, //不允许对文件进行重叠操作
NULL);
if(hidHandle==INVALID_HANDLE_VALUE)
{
hidHandle = CreateFile(devDetail->DevicePath,
// GENERIC_READ | GENERIC_WRITE,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0, //不允许对文件进行重叠操作
NULL);
}
}


// long ly=GetLastError();//debug
if (hidHandle==INVALID_HANDLE_VALUE)
{                   
SetupDiDestroyDeviceInfoList(hDevInfo);
free(devDetail);
return INVALID_HANDLE_VALUE;               



HIDD_ATTRIBUTES hidAttributes;
// hidAttributes.Size=sizeof(hidAttributes);//LY
if(!HidD_GetAttributes(hidHandle, &hidAttributes))//应用程序调用HID函数,传回厂商ID,产品ID与版本号
{  
free(devDetail); 
CloseHandle(hidHandle);                   
SetupDiDestroyDeviceInfoList(hDevInfo);                   
return INVALID_HANDLE_VALUE;               
}  


if   (USB_VID  == hidAttributes.VendorID//对比厂家ID              
  && USB_PID  == hidAttributes.ProductID//对比厂品ID
  && USB_PVN  == hidAttributes.VersionNumber)//对比软件ID
{                   
EventObject = CreateEvent(NULL, TRUE, TRUE, _T(""));//获得事件句柄,监听设备
//Set the members of the overlapped structure.
HIDOverlapped.Offset = 0;
HIDOverlapped.OffsetHigh = 0;
HIDOverlapped.hEvent = EventObject;
break;         
}               
else             
{                   
CloseHandle(hidHandle);//引用计数减1,当变为0时,系统删除内核对象
hidHandle   = INVALID_HANDLE_VALUE;
EventObject = NULL;
++deviceNo;               
}           
}       
}       
SetupDiDestroyDeviceInfoList(hDevInfo);       
return hidHandle; 
}

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值