USB HID类的编写
使用CH372 USB芯片进行USB数据通信时,CH372默认有2种模式,一种是内置固件模式,另外一种是外置固件模式。当设置CH372为外置固件模式时,上位机界面可以脱离调用该USB芯片公司提供的dll,使用微软提供的DDK文件提供的函数实现。
该类库有两个文件,分别是CHIDUSB.cpp和CHIDUSB.h。
CHIDUSB.h
1 /***************************************************
2 *作 者:温子祺
3 *联系方式:wenziqi@hotmail.com
4 *说 明:CHIDUSB.h
5 不要忘记引用以下的代码
6 #include "dbt.h"
7
8 extern "C" {
9 #include "hidsdi.h"
10 #include "setupapi.h"
11 }
12 #pragma comment(lib,"hid")
13 #pragma comment(lib,"setupapi")
14 ***************************************************/
15 #ifndef __CHIDUSB_H__
16 #define __CHIDUSB_H__
17
18 #include "dbt.h"
19
20 extern "C" {
21 #include "hidsdi.h"
22 #include "setupapi.h"
23 }
24
25 #pragma comment(lib,"hid")
26 #pragma comment(lib,"setupapi")
27
28
29
30 class CHIDUSB
31 {
32 public:
33 CHIDUSB();
34 virtual ~CHIDUSB();
35
36 BOOL Close(void);
37 BOOL Open (CWnd *pPortOwner,
38 DWORD VID,
39 DWORD PID,
40 UINT unUSBRecvBufSize,
41 UINT unRecvMsg,
42 UINT unConnectMsg
43 );
44
45 UINT Send(UCHAR *pSendBytes,UINT unSendLen);
46 UINT Recv(UCHAR *pRecvBytes);
47 void GetDevicePath(CString &str);
48
49 protected:
50
51 BOOL Ready(void)const; //USB是否已经打开
52 BOOL CreateThreadAndEvent(void); //创建线程和事件
53 static
54 DWORD RecvThread(LPVOID lpArg); //接收线程
55
56 void RegisterHIDDevice(CWnd *pPortOwner,GUID HidGuid);
57
58 private:
59
60 CWnd * m_pOwner;
61
62 BOOL m_bInit;
63
64 HIDP_CAPS m_Capabilities;
65
66 OVERLAPPED m_ovUSBRead;
67 OVERLAPPED m_ovUSBWrite;
68
69 HANDLE m_hUSBWrite;
70 HANDLE m_hUSBRead;
71 HANDLE m_hRecvExitEvent;
72
73 UCHAR *m_szUSBRecvBuf;
74 UINT m_unUSBRecvBufSize;
75 UINT m_unRecvLength;
76
77 UINT m_unRecvMsg;
78 UINT m_unConnectMsg;
79 CString m_strDevicePath;
80
81 };
82
83
84
85 #endif
CHIDUSB.cpp
1 /***************************************************
2 *作 者:温子祺
3 *联系方式:wenziqi@hotmail.com
4 *说 明:CHIDUSB.cpp
5 ***************************************************/
6 #include "StdAfx.h"
7 #include "CHIDUSB.h"
8 #include <assert.h> //使用断言
9
10 CHIDUSB::CHIDUSB()
11 {
12 m_pOwner=NULL;
13 m_hUSBWrite=NULL;
14 m_hUSBWrite=NULL;
15 m_hUSBRead=NULL;
16 m_hRecvExitEvent=NULL;
17 m_unRecvLength=0;
18 m_bInit=FALSE;
19 m_szUSBRecvBuf=NULL;
20 }
21
22 CHIDUSB::~CHIDUSB()
23 {
24 m_pOwner=NULL;
25 m_hUSBWrite=NULL;
26 m_hUSBWrite=NULL;
27 m_hUSBRead=NULL;
28 m_hRecvExitEvent=NULL;
29 m_szUSBRecvBuf=NULL;
30 }
31
32 BOOL CHIDUSB::Close(void)
33 {
34 m_bInit=FALSE;
35
36 if (m_hUSBRead)
37 {
38 CloseHandle(m_hUSBRead);
39 m_hUSBRead=NULL;
40 }
41
42 if (m_hUSBWrite)
43 {
44 CloseHandle(m_hUSBWrite);
45 m_hUSBWrite=NULL;
46 }
47
48 if (m_ovUSBRead.hEvent)
49 {
50 CloseHandle(m_ovUSBRead.hEvent);
51 m_ovUSBRead.hEvent=NULL;
52 }
53
54 if (m_ovUSBWrite.hEvent)
55 {
56 CloseHandle(m_ovUSBWrite.hEvent);
57 m_ovUSBWrite.hEvent=NULL;
58 }
59
60 if (m_hRecvExitEvent)
61 {
62 SetEvent(m_hRecvExitEvent); //退出线程
63 CloseHandle(m_hRecvExitEvent);
64 m_hRecvExitEvent=NULL;
65 }
66
67 if (m_pOwner)
68 {
69 m_pOwner=NULL;
70 }
71
72 if (m_szUSBRecvBuf)
73 {
74 delete []m_szUSBRecvBuf;
75 m_szUSBRecvBuf=NULL;
76 }
77
78
79 return TRUE;
80 }
81
82 BOOL CHIDUSB::Ready(void)const
83 {
84
85 return m_bInit;
86 }
87
88 BOOL CHIDUSB::CreateThreadAndEvent(void)
89 {
90 m_ovUSBRead.Offset=0;
91 m_ovUSBRead.OffsetHigh=0;
92 m_ovUSBRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
93
94 if (NULL==m_ovUSBRead.hEvent)
95 {
96 return FALSE;
97 }
98 else
99 {
100 ResetEvent(m_ovUSBRead.hEvent);
101 }
102
103
104 m_ovUSBWrite.Offset=0;
105 m_ovUSBWrite.OffsetHigh=0;
106 m_ovUSBWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
107
108 if (NULL==m_ovUSBWrite.hEvent)
109 {
110
111 return FALSE;
112 }
113 else
114 {
115 ResetEvent(m_ovUSBWrite.hEvent);
116 }
117
118
119 m_hRecvExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* 创建接收线程退出事件*/
120
121 if (NULL==m_hRecvExitEvent)
122 {
123 return FALSE;
124 }
125
126 ResetEvent(m_hRecvExitEvent); //设置线程没有退出
127
128 HANDLE hThread=NULL;
129 DWORD dwThreadID =0;
130
131 // 创建接收线程
132 hThread=CreateThread( 0,
133 0,
134 (LPTHREAD_START_ROUTINE)RecvThread,
135 this,
136 0,
137 &dwThreadID);
138
139 if (NULL==hThread)
140 {
141 return FALSE;
142 }
143
144 CloseHandle(hThread);
145 hThread=NULL;
146
147 return TRUE;
148 }
149
150 BOOL CHIDUSB::Open(CWnd *pPortOwner,
151 DWORD VID,
152 DWORD PID,
153 UINT unUSBRecvBufSize,
154 UINT unRecvMsg,
155 UINT unConnectMsg)
156 {
157 assert(NULL != pPortOwner);
158
159 m_pOwner = pPortOwner;
160
161 if (Ready())
162 {
163 Close();
164 }
165
166 if (!m_szUSBRecvBuf)
167 {
168 m_szUSBRecvBuf=new UCHAR[unUSBRecvBufSize];
169 m_unUSBRecvBufSize=unUSBRecvBufSize;
170 }
171
172
173 ULONG Required;
174 //定义strUsbPath 设备路径
175 //CString strUsbPath;
176 //定义一个GUID的结构体HidGuid来保存HID设备的接口类GUID。
177 GUID HidGuid;
178 //定义一个DEVINFO的句柄hDevInfoSet来保存获取到的设备信息集合句柄。
179 HDEVINFO hDevInfoSet;
180 //定义MemberIndex,表示当前搜索到第几个设备,0表示第一个设备。
181 DWORD MemberIndex;
182 //DevInfoData,用来保存设备的驱动接口信息
183 SP_DEVICE_INTERFACE_DATA DevInfoData;
184 //定义一个BOOL变量,保存函数调用是否返回成功
185 BOOL Result;
186 //定义一个RequiredSize的变量,用来接收需要保存详细信息的缓冲长度。
187 DWORD RequiredSize;
188 //定义一个指向设备详细信息的结构体指针。
189 PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetailData;
190 //定义一个用来保存打开设备的句柄。
191 HANDLE DevHandle;
192 //定义一个HIDD_ATTRIBUTES的结构体变量,保存设备的属性。
193 HIDD_ATTRIBUTES DevAttributes;
194
195 // Request to receive messages when a device is attached or removed.
196 // Also see WM_DEVICECHANGE in BEGIN_MESSAGE_MAP(CUsbhidiocDlg, CDialog).
197 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
198
199 //对DevInfoData结构体的cbSize初始化为结构体大小
200 DevInfoData.cbSize=sizeof(DevInfoData);
201 //对DevAttributes结构体的Size初始化为结构体大小
202 DevAttributes.Size=sizeof(DevAttributes);
203 //根据HidGuid来获取设备信息集合。其中Flags参数设置为
204 //DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,前者表示使用的GUID为
205 //接口类GUID,后者表示只列举正在使用的设备,因为我们这里只
206 //查找已经连接上的设备。返回的句柄保存在hDevinfo中。注意设备
207 //信息集合在使用完毕后,要使用函数SetupDiDestroyDeviceInfoList
208 //销毁,不然会造成内存泄漏。
209
210 //调用HidD_GetHidGuid函数获取HID设备的GUID,并保存在HidGuid中。
211 HidD_GetHidGuid(&HidGuid);
212
213 hDevInfoSet=SetupDiGetClassDevs(&HidGuid,
214 NULL,
215 NULL,
216 DIGCF_DEVICEINTERFACE|DIGCF_PRESENT);
217 //然后对设备集合中每个设备进行列举,检查是否是我们要找的设备
218 //当找到我们指定的设备,或者设备已经查找完毕时,就退出查找。
219 //首先指向第一个设备,即将MemberIndex置为0。
220 MemberIndex=0;
221
222
223 while(1)
224 {
225 //调用SetupDiEnumDeviceInterfaces在设备信息集合中获取编号为
226 //MemberIndex的设备信息。
227 Result=SetupDiEnumDeviceInterfaces(hDevInfoSet,
228 0,
229 &HidGuid,
230 MemberIndex,
231 &DevInfoData);
232
233 //如果获取信息失败,则说明设备已经查找完毕,退出循环。
234 if(Result==FALSE) break;
235
236 //将MemberIndex指向下一个设备
237 MemberIndex++;
238
239 //如果获取信息成功,则继续获取该设备的详细信息。在获取设备
240 //详细信息时,需要先知道保存详细信息需要多大的缓冲区,这通过
241 //第一次调用函数SetupDiGetDeviceInterfaceDetail来获取。这时
242 //提供缓冲区和长度都为NULL的参数,并提供一个用来保存需要多大
243 //缓冲区的变量RequiredSize。
244 Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
245 &DevInfoData,
246 NULL,
247 0,
248 &RequiredSize,
249 NULL);
250
251 //然后,分配一个大小为RequiredSize缓冲区,用来保存设备详细信息。
252 pDevDetailData=(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize);
253
254 if(pDevDetailData==NULL) //如果内存不足,则直接返回。
255 {
256 TRACE("内存不足!");
257 SetupDiDestroyDeviceInfoList(hDevInfoSet);
258 return FALSE;
259 }
260
261 //并设置pDevDetailData的cbSize为结构体的大小(注意只是结构体大小,
262 //不包括后面缓冲区)。
263 pDevDetailData->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
264
265 //然后再次调用SetupDiGetDeviceInterfaceDetail函数来获取设备的
266 //详细信息。这次调用设置使用的缓冲区以及缓冲区大小。
267 Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
268 &DevInfoData,
269 pDevDetailData,
270 RequiredSize,
271 &Required,
272 NULL);
273
274 //将设备路径复制出来,然后销毁刚刚申请的内存。
275 m_strDevicePath=pDevDetailData->DevicePath;
276 free(pDevDetailData);
277
278 //如果调用失败,则查找下一个设备。
279 if(Result==FALSE) continue;
280
281 //如果调用成功,则使用不带读写访问的CreateFile函数
282 //来获取设备的属性,包括VID、PID、版本号等。
283 //对于一些独占设备(例如USB键盘),使用读访问方式是无法打开的,
284 //而使用不带读写访问的格式才可以打开这些设备,从而获取设备的属性。
285 DevHandle=CreateFile(m_strDevicePath,
286 0,
287 FILE_SHARE_READ|FILE_SHARE_WRITE,
288 (LPSECURITY_ATTRIBUTES)NULL,
289 OPEN_EXISTING,
290 0,
291 NULL);
292
293 //如果打开成功,则获取设备属性。
294 if(DevHandle!=INVALID_HANDLE_VALUE)
295 {
296 //获取设备的属性并保存在DevAttributes结构体中
297 Result=HidD_GetAttributes(DevHandle,
298 &DevAttributes);
299
300 //获取失败,查找下一个
301 if(Result==FALSE)
302 {
303 //关闭刚刚打开的设备
304 CloseHandle(DevHandle);
305 DevHandle=NULL;
306 continue;
307 }
308 //如果获取成功,则将属性中的VID、PID以及设备版本号与我们需要的
309 //进行比较,如果都一致的话,则说明它就是我们要找的设备。
310 if(DevAttributes.VendorID==VID) //如果VID相等
311 if(DevAttributes.ProductID==PID) //并且PID相等
312 //if(DevAttributes.VersionNumber==StUsbID.m_dwPVN) //并且设备版本号相等
313 {
314 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
315 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
316 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid;
317 //获取设备属性结构体
318
319 PHIDP_PREPARSED_DATA PreparsedData;
320
321 HidD_GetPreparsedData(DevHandle,
322 &PreparsedData
323 );
324
325 HidP_GetCaps (PreparsedData,
326 &m_Capabilities
327 );
328 //释放资源
329 HidD_FreePreparsedData(PreparsedData);
330
331 //那么就是我们要找的设备,分别使用读写方式打开之,并保存其句柄
332 //并且选择为异步访问方式。
333 //读方式打开设备
334
335 m_hUSBRead=CreateFile(m_strDevicePath,
336 GENERIC_READ,
337 FILE_SHARE_READ|FILE_SHARE_WRITE,
338 (LPSECURITY_ATTRIBUTES)NULL,
339 OPEN_EXISTING,
340 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
341 NULL);
342
343 if (INVALID_HANDLE_VALUE==m_hUSBRead)
344 {
345 TRACE("读访问打开HidUsb设备失败......!");
346 }
347 else
348 {
349 TRACE("读访问打开HidUsb设备成功......!");
350
351 }
352
353 //写方式打开设备
354 m_hUSBWrite=CreateFile(m_strDevicePath,
355 GENERIC_WRITE,
356 FILE_SHARE_READ|FILE_SHARE_WRITE,
357 (LPSECURITY_ATTRIBUTES)NULL,
358 OPEN_EXISTING,
359 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
360 NULL);
361
362 if (INVALID_HANDLE_VALUE==m_hUSBWrite)
363 {
364 TRACE("写访问打开HidUsb设备失败......!");
365 }
366 else
367 {
368 TRACE("写访问打开HidUsb设备成功......!");
369
370 }
371
372 if (m_hUSBRead == INVALID_HANDLE_VALUE
373 &&m_hUSBWrite == INVALID_HANDLE_VALUE)
374 {
375 return FALSE;
376 }
377
378 if (!CreateThreadAndEvent())
379 {
380 return FALSE;
381 }
382
383 m_bInit=TRUE;
384
385 m_unRecvMsg=unRecvMsg;
386 m_unConnectMsg=unConnectMsg;
387
388 RegisterHIDDevice(pPortOwner,HidGuid);
389
390 return TRUE;
391 }
392 }
393 //如果打开失败,则查找下一个设备
394 else continue;
395 }
396
397
398 //调用SetupDiDestroyDeviceInfoList函数销毁设备信息集合
399 SetupDiDestroyDeviceInfoList(hDevInfoSet);
400
401 return FALSE;
402
403 }
404 UINT CHIDUSB::Send(UCHAR *pSendBytes,UINT unSendLen)
405 {
406 if (NULL == pSendBytes ||0==unSendLen)
407 {
408 return 0;
409 }
410
411 if (m_hUSBWrite==INVALID_HANDLE_VALUE \
412 ||m_hUSBWrite==NULL)
413 {
414 return 0;
415 }
416
417 UCHAR szSendBuf[65] ={0};
418 DWORD dwSendBytes=0;
419 INT rt=0;
420
421 //HID发送报告第一个字节必须为0
422 //所以发送总长度为0x41=65
423
424 memcpy(&szSendBuf[1],pSendBytes,64);
425
426 rt=WriteFile(m_hUSBWrite,
427 szSendBuf,
428 m_Capabilities.OutputReportByteLength,
429 NULL,
430 &m_ovUSBWrite);
431
432 WaitForSingleObject(m_ovUSBWrite.hEvent,3000);
433 ResetEvent (m_ovUSBWrite.hEvent);
434
435 GetOverlappedResult(m_hUSBWrite,&m_ovUSBWrite,&dwSendBytes,TRUE);
436
437 return (UINT)(dwSendBytes-1);
438 }
439
440 UINT CHIDUSB::Recv(UCHAR *pRecvBytes)
441 {
442 if (NULL == pRecvBytes || 0==m_unRecvLength)
443 {
444 return 0;
445 }
446
447 if (m_hUSBRead==INVALID_HANDLE_VALUE \
448 ||m_hUSBRead==NULL)
449 {
450 return 0;
451 }
452
453 UINT unRecvLength=m_unRecvLength;
454
455 m_unRecvLength=0;
456
457 memcpy(pRecvBytes,m_szUSBRecvBuf,64);
458
459 return unRecvLength;
460 }
461
462 DWORD CHIDUSB::RecvThread(LPVOID lpArg)
463 {
464 assert(NULL != lpArg);
465
466 CHIDUSB *pArg=(CHIDUSB *)lpArg;
467
468 assert(NULL != pArg);
469
470 UCHAR szRecvBuf[65]={0};
471 DWORD dwRecvBytes=0;
472
473 while(1)
474 {
475 if (WaitForSingleObject(pArg->m_hRecvExitEvent,0)==WAIT_OBJECT_0)
476 {
477 break; //线程退出
478 }
479
480 if (pArg->Ready())
481 {
482 memset(pArg->m_szUSBRecvBuf,0,sizeof(pArg->m_szUSBRecvBuf));
483 memset(szRecvBuf,0,sizeof(szRecvBuf));
484
485 ReadFile(pArg->m_hUSBRead,
486 szRecvBuf,
487 pArg->m_Capabilities.InputReportByteLength,
488 NULL,
489 &pArg->m_ovUSBRead
490 );
491
492 WaitForSingleObject(pArg->m_ovUSBRead.hEvent,INFINITE);
493 ResetEvent(pArg->m_ovUSBRead.hEvent);
494 //通过GetOverlappedResult函数来获取实际读取到的字节数。
495 GetOverlappedResult(pArg->m_hUSBRead,&pArg->m_ovUSBRead,&dwRecvBytes,TRUE);
496
497 if (dwRecvBytes)
498 {
499 memcpy(pArg->m_szUSBRecvBuf,&szRecvBuf[1],64);
500 pArg->m_unRecvLength=(UINT)(dwRecvBytes-1);//默认返回65个字节,所以要减1
501 dwRecvBytes=0;
502 //完成这个消息才进行下个操作,因而不需要加上同步事件。
503 //如果使用PostMessage()就需要加上同步事件
504 ::SendMessage((pArg->m_pOwner)->m_hWnd,
505 pArg->m_unRecvMsg,
506 0,
507 0);
508
509 }
510
511 }
512 Sleep(1);
513 }
514
515 Sleep(10);
516
517 return 0;
518 }
519
520 void CHIDUSB::GetDevicePath(CString &str)
521 {
522 str=m_strDevicePath;
523 }
524
525 void CHIDUSB::RegisterHIDDevice(CWnd *pPortOwner,GUID HidGuid)
526 {
527 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
528 HDEVNOTIFY DeviceNotificationHandle;
529
530 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
531 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
532 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid;
533
534 DeviceNotificationHandle =
535 RegisterDeviceNotification(pPortOwner->m_hWnd, &DevBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE);
536 }
537
538