Wednesday, April 27, 2011

APN Setting for BlackBerry



APN Setting for BlackBerry

Posted on December 6, 2010 at 9:06 pm by ironmusc
Here are the APN (Access Point Name) settings on the Blackberry handset, so we will not be subjected to additional GPRS cost, because we actually have subscribed BlackBerry Internet Service packages either daily, weekly or monthly as the packages are packed all the services are unlimited.
But sometimes we receive a bill or the circumstances in which our credit truncated due to the cost of GPRS usage, therefore please check the APN settings that exist on our BlackBerry.
The trick is to go into the menu as follows:
OPTIONS> ADVANCE OPTIONS> TCP (click)
Username and Password leave blank
There are 3 options:
1. APN: blackberry.net
So the BlackBerry browser will only be able to access sites which are friendly with BlackBerry. BlackBerry friendly site would not be subjected to GPRS charge. For the sites that are not Blackberry friendly, it can not be accessed.
2. APN: [blank]
So the Blackberry browser can access both, if the site was BlackBerry friendly, it does not pay. If you get into sites that are not BlackBerry friendly, it will be charged the cost of GPRS, like streaming radio sites, Youtube or etc.
3. APN: wap.-operator-
This is not recommended because, all accessible sites are charge by GPRS, even though it was BB friendly.
Then, in the Blackberry browser, we must change the existing configuration like this:
1. OPTIONS> ADVANCE OPTIONS> BROWSER (click).
2. Default browser configuration (CLICK): select INTERNET BROWSER.
Tip: To access youtube, radio and other streaming video, you should use Wi-Fi access, so there will be no additional charges for GPRS.

Monday, April 25, 2011

Compiling for 64 bit on a 32 bit machine

http://social.msdn.microsoft.com/Forums/en/netfx64bit/thread/f21450f5-b36d-4ea2-9806-f169aff0388d

Goto Add/Remove programs, select Visual Studio, Change, Add/Remove features and add x64 compilers from C++ node. After installing x64 compilers, you should see appropriate options under Platform and shoudl be able to build x64 projects on 32 bit machine.

Tuesday, April 19, 2011

用人机接口类型开发USB设备

http://hi.baidu.com/qwestw/blog/item/2d7ae23d97a41b08bba1675a.html


发布日期:2005-07-13  -->作者:陈芳 王毅 来源:微计算机信息
1 引言
USB是目前发展应用非常广泛的一项技术。它是一种计算机系统连接外围设备的标准输入/输出接口。根据外围设备的不同的类型USB协议将其分类,每个设备类型都定义了类似功能设备的共同行为和协议。相同类型的设备都由一组标准定义的功能模块组成。这样主机与USB设备之间的通信就可以通过一些标准格式的数据包来完成。USB开发者论坛发布了一系列USB设备的类型定义,并配以相应的使用说明。下面表格显示出 USB的设备类型(DEVEICE CLASS):
表1 USB的设备类型
虽然Windows已经提供了底层总线操作的驱动程序,但与此类底层驱动程序接口的是i/o请求包的IRPs的结构,而Windows为应用程序提供的接口是API函数。因此必须在其间建立一个驱动程序,在USB底层驱动与Windows应用程序之间传递消息。VB、 C/C++、Delphi等通用编程语言编写的应用程序都可以在设备驱动程序的支持下,调用Readfile、WriteFile、DeviceIoControl等API函数。而编写底层总线的驱动程序是非常复杂的一项工程。为了消除编写设备驱动程序的问题,可对于一些具有相似功能的设备可以组成一类,分享共有的特性,便于使用Windows提供共同的类驱动程序。
2 HID类型概述
    第一个被windows支持的usb外围设备类是人机接口设备。hid是human interface device人机接口设备的英文缩写。是指直接和人进行互动的设备。如鼠标键盘等。运行在WINDOWS98或其他更高的版本的操作系统的PC机,系统除了提供通用的USB设备的底层驱动以外,还单独提供了一些HID设备的完整驱动,应用程序可以很容易的与操作系统内部的hid通讯。这样使得符合hid类的USB设备很容易开发与运行。也就是说,我们如果想实现一个USB的HID类设备,是不需要在Windows下开发自己的驱动程序。HID不一定要是标准的外设类型,唯一的要求是交换的数据存储在报文的结构内,设备固件必须支持报文的格式。任何工作在该限制之内的设备都可以成为一个hid,例如温度计,电压计,读卡机等。
hid类设备只能使用控制传输与中断传输两种方式。HID的交换的数据格式称为报文。报文形式灵活,能处理任何类型的数据。HID特有的请求,Set_Report和Get_Report为主机和设备之间的任何类型数据块传输提供了一种方法。主机发出Get_Report请求,设备响应向主机传送数据块;主机发出Set_Report请求,设备响应准备接收主机发出的数据块。对于一个全速设备,中断传输方式下每笔事务能够传送的最大数据量是64字节,全速设备每毫秒不能有超过一笔事务,所以每秒最多传送64000字节。高速设备,每笔事务能够传送的最大数据量是1024字节。对于不能一次传输完毕的数据,接收和发送报文可以采用多笔事务。
    表2列举出了与HID类设备通信过程中使用到的大量函数,这些函数的用法在DDK的帮助文档中均有详细地解释。这些函数包含在Hid.dll、Setupapi.dll、Kernel32.dll三个动态链接库中,分别起到与HID设备通讯,寻找与识别设备,交换数据的作用。

表2 HID设备通信相关API函数
Visual Basic编程语言,语法简单用法灵活易于掌握。用它来开发通信程序是一种非常快捷的途径。但是需要指出的是,API函数的声明格式在DDK的文档内全部是采用C语言的格式声明,而VB与C语言的在变量的声明、存储格式上都有很大的区别。所以首先需要进行类型转换,例如CHAR转换为Byte类型,USHORT、ULONG、BOOLEAN、LP_(长指针前缀)、P_(指针前缀)转换为Long 类型,PCTSTR转换为String类型。其次,C语言的结构体可以转换为VB中的自定义数据类型,并且特别值得注意,在涉及到自定义数据类型中子类存储位置必须注意到VB与C语言对应转换类型所占内存空间不同的问题。最后C语言中有指针的概念,而VB中没有,所以需用Byref按地址传递,VarPtr取变量地址等方法进行相应的操作。
3 VB中调用API函数详述  
第一步需要获得GUID(global unique identifier),需要调用函数 HidD_GetHidGuid ,他在可以如下定义:
Public Declare Function HidD_GetHidGuid Lib_
"hid.dll" _
(ByRef HidGuid As GUID)
As Long       
通过调用它可以得到HID 类设备的 GUID,应用程序在与HID设备通讯之前,必须获取HID类的独特标志符GUID,它是一个128位值,每一位唯一表示了一个对象。通过这个API函数就可以从系统中读取该值,得到 HID 设备句柄。接下来应检测符合设定参数的HID,应使用函数 SetupDiGetClassDevs,转换为VB中的定义是:
Public Declare Function SetupDiGetClassDevs_
Lib "setupapi.dll" _
Alias "SetupDiGetClassDevsA" _
(ByRef ClassGuid As GUID, _
ByVal Enumerator As String, _
ByVal hwndParent As Long, _
ByVal Flags As Long) _
As Long         
当调用该函数成功是,他返回一个包含所有的与设定参数匹配如:已连接和列举,的设备信息的结构体数组地址,该值在下一个将要调用的函数SetupDiEnumDeviceInterfaces中将使用到。SetupDiEnumDeviceInterfaces可定义为:
Public Declare Function_
SetupDiEnumDeviceInterfaces Lib_
"setupapi.dll" _
(ByVal DeviceInfoSet As Long, _
ByVal DeviceInfoData As Long, _
ByRef InterfaceClassGuid As GUID, _
ByVal MenberIndex As Long, _
ByRef DeviceInterfaceData As_
SP_DEVICE_INTERFACE_DATA) _
As Long
这样通过上面函数SetupDiGetClassDevs给出的设备信息得到的设备的一个接口的地址,每一次调用必需传递一个数组的索引来指定一个接口。上例中SP_DEVICE_INTERFACE
_DATA包含的结构用来识别每一个HID的接口。要与设备通信还需要一些更详细的信息,其中最重要的是设备路径,它可以通过函数SetupDiGetDeviceInterfaceDetail得到。其定义格式如下:
Public Declare Function_
SetupDiGetDeviceInterfaceDetail Lib_
"setupapi.dll" _
Alias "SetupDiGetDeviceInterfaceDetailA" _
(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
    其中的DeviceInterfaceData是自定义类型:
Public Type_
SP_DEVICE_INTERFACE_DETAIL_DATA
    cbSize As Long
    DevicePath As String
End Type
这样函数就可传回与前一函数所识别的接口有关的结构体,其中DevicePath是一个设备路径,应用通过上面函数SetupDiEnumDevice-
Interfaces给出的设备接口数据设定设备接口的详细信息。第一次调用该函数时,其中的DeviceInterfaceDetailDataSize无法预知,故可以两次调用该函数,第一次调用出错,但可以返回正确的DeviceInterfaceDetailDataSize ,第二次调用传递返回值,调用即可成功。通过以上步骤基本可以建立与设备的连接了。
    如想获得更多关于设备能力的信息,还可以使用HidD_GetAttributes函数,HidP_Get-
PreparsedData函数,HidP_GetPreparsedData函数,他们都是包含在hid.dll文件中的。分别可以实现获得厂商ID,产品ID,Usage, Usage Page,报文长度等,不再一一赘述。
    为了开启一个HID设备需用到函数CreateFile,以取得设备代号,并用此代号与设备交换数据。定义形式如下:
Public Declare Function CreateFile Lib_
"kernel32" Alias "CreateFileA" _
(ByVal lpFilename As Long, _
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
    调用函数CreateFile以A结尾,是因为处理字符串时采用了8位的ANSI码。
当应用程序取得HID设备的代号,此时就可以读写报文,利用通用函数WriteFile与ReadFile
定义如下所示:
Public Declare Function WriteFile Lib_
"kernel32" _
(ByVal hFile As Long, _
ByRef lpBuffer As Byte, _
ByVal nNumberOfBytesToWrite As Long, _
ByRef lpNumberOfBytesWritten As Long, _
ByVal lpOverlapped As Long) _
As Long
Declare Function ReadFile Lib "kernel32" _
(ByVal hFile As Long, _
ByRef lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, _
ByRef lpNumberOfBytesRead As Long, _
lpOverlapped As Long) _
As Long
读写报文缓冲区时,第一个字节是Report ID,其后是报文数据。报文缓冲区默认是八个报文,并且环状排列。因为数据读写是发生在主机轮训设备的时候,并不是由设备触发产生硬件中断,所以如不能及时读写,新的数据会覆盖旧的数据,导致生报文丢失。当数据读写频繁时应使用特征报文,它可以保证当报文数据没有变化时,HID不会传送新的数据。
当应用程序结束与HID的通信后,必须释放所有之前保留的资源。所涉及到的几个API函数如下:其中包括HidD_FreePreparsedData ,如下定义:
Public Declare Function_
HidD_FreePreparsedData Lib "hid.dll" _
(ByRef PreparsedData As Long) _
As Long
其作用是清除函数HidP_GetPreparsedData传回的PreparsedData数据所占用的缓冲区;函数SetupDiDestroyDeviceInfoLis定义为:
Public Declare Function_
SetupDiDestroyDeviceInfoList Lib_
"setupapi.dll" _
(ByVal DeviceInfoSet As Long) _
As Long
当不再使用SetupDiGetClassDevs时,应用上述函数释放其返回的数组hDevInfo数组。还有需要使用函数CloseHandle,它是一个非常通用的API函数,可以用于关闭通信。
4 小结
    实际编程表明使用VB开发HID类USB设备是一条非常方便快捷的途径,不需要编写底层驱动,涉及到的API函数多是大家熟知的通用用函数。但同时还必须注意到一点,HID类只支持控制传输与中断传输。控制传输通常不用于数据的传输,而中断传输的特点是保证最大延迟,也就是事务之间的时间。他没有保证传输速率,而是保证每笔事务之间的时间不会超过最大延时。所以中断传输适用于数据量不太大,但需要及时快速的传送,实时性要求较高的场合。对于数据量比较大的传输,是不宜使用中断传输的。因此HID设备应用的场合也必须根据该特点灵活使用。
参考文献:
[1] 陈逸等 USB大全[M] 中国电力出版社 2001.8
[2] Universal Serial Bus Specification [M] 2000.4
[3] 萧世文 USB2.0硬件设计 [M] 清华大学出版社,2002.10

Windows主机端与自定义USB HID设备通信详解

Ref: http://blogold.chinaunix.net/u3/98810/showart_1963291.html


说明:
-          以下结论都是基于Windows XP系统所得出的,不保证在其他系统的适用性。
-          在此讨论的是HID自定义设备,对于标准设备,譬如USB鼠标和键盘,由于操作系统对其独占,许多操作未必能正确执行。


1  所使用的典型Windows API
CreateFile
ReadFile
WriteFile
以下函数是DDK的内容:
HidD_SetFeature
HidD_GetFeature
HidD_SetOutputReport
HidD_GetInputReport
其中,CreateFile用于打开设备;ReadFileHidD_GetFeatureHidD_GetInputReport用于设备到主机方向的数据通信;WriteFileHidD_SetFeatureHidD_SetOutputReport用于主机到设备方向的数据通信。鉴于实际应用,后文主要讨论CreateFileWriteFileReadFileHidD_SetFeature四个函数,明白了这四个函数,其它的可以类推之。


2  几个常见错误
       当使用以上API时,如果操作失败,调用GetLastError()会得到以下常见错误:
       6         句柄无效
       23       数据错误(循环冗余码检查)
       87       参数错误
       1784    用户提供的buffer无效
       后文将会详细说明这些错误情况。


3.         主机端设备枚举程序流程


4.         函数使用说明
CreateFile(devDetail->DevicePath,                                         //设备路径
               GENERIC_READ | GENERIC_WRITE,                    //访问方式
               FILE_SHARE_READ | FILE_SHARE_WRITE,         //共享模式
               NULL,
               OPEN_EXISTING,                                           //文件不存在时,返回失败
               FILE_FLAG_OVERLAPPED,                                 //以重叠(异步)模式打开
               NULL);

在这里,CreateFile用于打开HID设备,其中设备路径通过函数SetupDiGetInterfaceDeviceDetail取得。CreateFile有以下几点需要注意:

-     访问方式: 如果是系统独占设备,例如鼠标、键盘等等,应将此参数设置为0,否则后续函数操作将失败(譬如HidD_GetAttributes);也就是说,不能对独占设备进行除了查询以外的任何操作,所以能够使用的函数也是很有限的,下文的一些函数并不一定适合这些设备。在此顺便列出MSDN上关于此参数的说明:
If this parameter is zero, the application can query file and device attributes without accessing the device. This is useful if an application wants to determine the size of a floppy disk drive and the formats it supports without requiring a floppy in the drive. It can also be used to test for the file's or directory's existence without opening it for read or write access
-          重叠(异步)模式:此参数并不会在此处表现出明显的意义,它主要是对后续的WriteFileReadFile有影响。如果这里设置为重叠(异步)模式,那么在使用WriteFileReadFile时也应该使用重叠(异步)模式,反之亦然。这首先要求WriteFileReadFile的最后一个参数不能为空(NULL)。否则,便会返回87(参数错误)错误号。当然,87号错误并不代表就是此参数不正确,更多的信息将在具体讲述这两个函数时指出。此参数为0时,代表同步模式,即WriteFileReadFile操作会在数据处理完成之后才返回,否则阻塞在函数内部。

ReadFile(hDev,                                 //设备句柄,即CreateFile的返回值
              recvBuffer,                          //用于接收数据的buffer
              IN_REPORT_LEN,              //要读取数据的长度
              &recvBytes,                         //实际收到的数据的字节数
              &ol);                                  //异步模式

在这里,ReadFile用于读取HID设备通过中断IN传输发来的输入报告。有以下几点要注意:

1ReadFile的调用不会引起设备的任何反应,即HID设备与主机之间的中断IN传输不与ReadFile打交道。实际上主机会在最大间隔时间(由设备的端点描述符来指定)内轮询设备,发出中断IN传输的请求。“读取”即意味着从某个buffer里面取回数据,实际上这个buffer就是HID设备驱动中的buffer。这个buffer的大小可以通过HidD_SetNumInputBuffers来改变。在XP上缺省值是32(个报告)。

2、读取的数据对象是输入报告,也即通过中断输入管道传入的数据。所以,如果设备不支持中断IN传输,那么是无法使用此函数来得到预期结果的。实际上这种情况不可能在HID中出现,因为协议指明了至少要有一个中断IN端点。

3IN_REPORT_LEN代表要读取的数据的长度(实际的数据正文+一个byte的报告ID),这里是一个常数,主要是因为设备固件的信息我是完全知道的,当然知道要读取多少数据(也就是报告的长度);不过也可以通过另外的函数(HidD_GetPreparsedData)来事先取得报告的长度,这里不做详细讨论。因为很难想象在不了解固件信息的情况下来做自定义设备的HID通信,在实际应用中一般来说就是固件与PC程序匹配着来开发。此参数如果设置过大,不会有实质性的错误,在recvBytes参数中会输出实际读到的长度;如果设置过小,即小于报告的长度,会返回1784号错误(用户提供的buffer无效)。

4、关于异步模式。前面已经提过,此参数的设置必须与CreateFile时的设置相对应,否则会返回87号错误(参数错误)。如果不需要异步模式,此参数需置为NULL。在这种情况下,ReadFile会一直等待直到数据读取成功,所以会阻塞住程序的当前过程。

       WriteFile(hDev,                                 //设备句柄,即CreateFile的返回值
                     reportBuf,                           //存有待发送数据的buffer
                     OUT_REPORT_LEN,           //待发送数据的长度
                     &sendBytes,                        //实际收到的数据的字节数
                     &ol);                                  //异步模式

       在这里,WriteFile用于传输一个输出报告HID设备。有以下几点要注意:

1、  ReadFile不同,WriteFile函数被调用后,虽然也是经过驱动程序,但是最终会反映到设备中。也就是说,调用WriteFile后,设备会接收到输出报告的请求。如果设备使用了中断OUT传输,则WriteFile会通过中断OUT管道来进行传输;否则会使用SetReport请求通过控制管道来传输。

2、  OUT_REPORT_LEN代表要写入的数据长度(实际的数据正文+一个byte的报告ID)。如果大于实际报告的长度,则使用实际报告长度;如果小于实际报告长度,会返回1784号错误(用户提供的buffer无效)。

3、  reportBuf[0]必须存有待发送报告的ID,并且此报告ID指示的必须是输出报告,否则会返回87号错误(参数错误)。这种情况可能容易被程序员忽略,结果不知错误号所反映的是什么,网上也经常有类似疑问的帖子。顺便指出,输入报告、输入报告、特征报告这些报告类型,是反映在HID设备的报告描述符中。后文将做举例讨论。

4、  关于异步模式。前面已经提过,此参数的设置必须与CreateFile时的设置相对应,否则会返回87号错误(参数错误)。如果不需要异步模式,此参数需置为NULL。在这种情况下,WriteFile会一直等待直到数据读取成功,所以会阻塞住程序的当前过程。

HidD_SetFeature(hDev,                                    //设备句柄,即CreateFile的返回值
                     reportBuf,                                   //存有待发送数据的buffer
                     FEATURE_REPORT_LEN);        //buffer的长度
HidD_SetOutputReport(hDev,                            //设备句柄,即CreateFile的返回值
                     reportBuf,                                   //存有待发送数据的buffer
                     OUT_REPORT_LEN);                //buffer的长度

HidD_SetFeature发送一个特征报告给设备,HidD_ SetOutputReport发送一个输出报告给设备。注意以下几点:

1、  WriteFile类似,必须在reportBuf[0]中指明要发送的报告的ID,并且和各自适合的类型相对应。也就是说,HidD_SetFeature只能发送特征报告,因此报告ID必须是特征报告的IDHidD_SetOutputReport只能发送输出报告,因此报告ID只能是输出报告的ID
2、  这两个函数最常返回的错误代码是23(数据错误)。包括但不仅限于以下情况:
报告ID与固件描述的不符。
传入的buffer长度少于固件描述的报告的长度。
据有关资料反映(非官方文档),只要是驱动程序对请求无反应,都会产生此错误。


5.         常见错误汇总
- HID ReadFile
  - Error Code 6 (handle is invalid)
    传入的句柄无效
  - Error Code 87 (参数错误)
    很可能是createfile时声明了异步方式,但是读取时按同步读取。
  - Error Code 1784 (用户提供的buffer无效):
    传参时传入的“读取buffer长度”与实际的报告长度不符。

- HID WriteFile
  - Error Code 6 (handle is invalid)
    传入的句柄无效
  - Error Code 87(参数错误)
    - CreateFile时声明的同步/异步方式与实际调用WriteFile时传入的不同。
    报告ID与固件中定义的不一致(buffer的首字节是报告ID
  - Error Code 1784 (用户提供的buffer无效)
    传参时传入的“写入buffer长度”与实际的报告长度不符。

- HidD_SetFeature
- HidD_SetOutputReport
  - Error Code 1 (incorrect function)
    不支持此函数,很可能是设备的报告描述符中未定义这样的报告类型(输入、输出、特征)
  - Error Code 6 (handle is invalid)
    传入的句柄无效
  - Error Code 23(数据错误(循环冗余码检查))
    报告ID与固件中定义的不相符(buffer的首字节是报告ID
    传入的buffer长度少于固件定义的报告长度(报告正文+1byte, 1byte为报告ID
    据相关资料反映(非官方文档),只要是驱动程序不接受此请求(对请求无反应),都会产生此错误


6.         报告描述符及数据通信程序示例
报告描述符(由于是汇编代码,所以不必留意其语法,仅需注意表中的每个数据都占1个字节):

_ReportDescriptor:                            //报告描述符
       .dw 0x06,  0x00, 0xff                 //用法页
    .dw 0x09,  0x01                            //用法(供应商用法1)
    .dw 0xa1,  0x01                            //集合开始
    .dw 0x85,  0x01                            //报告ID(1)
    .dw 0x09,  0x01                            //用法(供应商用法1)  
    .dw 0x15,  0x00                            //逻辑最小值(0)
    .dw 0x26,  0xff, 0x0                      //逻辑最大值(255)
    .dw 0x75,  0x08                            //报告大小(8)
    .dw 0x95,  0x07                            //报告计数(7)
    .dw 0x81,  0x06                            //输入(数据,变量,相对值)
   
    .dw 0x09,  0x01                          //用法(供应商用法1)  
    .dw 0x85,  0x03                          //报告ID3
    .dw 0xb1,  0x06                          //特征(数据,变量,相对值)

       .dw 0x09,  0x01                      //用法(供应商用法1)
    .dw 0x85,  0x02                         //报告ID2
    .dw 0xb1,  0x06                         //特征(数据,变量,相对值)
   
    .dw 0x09,  0x01                         //用法(供应商用法1)  
    .dw 0x85,  0x04                         //报告ID4
    .dw 0x91,  0x06                         //输出(数据,变量,相对值)
    .dw   0xc0                                  //结合结束
_ReportDescriptor_End:

这个报告描述符,定义了4个不同的报告:输入报告1,特征报告2,特征报告3,输出报告4(数字代表其报告ID)。为了简化,每个报告都是7个字节(加上报告ID就是8个字节)。下面用一个简单的示例来描述PC端与USB HID设备进行通信的一般方法。
 
#define USB_VID 0xFC0
#define USB_PID 0x420
HANDLE OpenMyHIDDevice(int overlapped);

void HIDSampleFunc()
{
    HANDLE        hDev;
    BYTE        recvDataBuf[8];
    BYTE        reportBuf[8];
    DWORD        bytes;
     
    hDev = OpenMyHIDDevice(0); //打开设备,不使用重叠(异步)方式;
    
    if (hDev == INVALID_HANDLE_VALUE)
        return;
    
    reportBuf[0] = 4; //输出报告的报告ID是4
    memset(reportBuf, 0, 8);
    reportBuf[1] = 1;
    if (!WriteFile(hDev, reportBuf, 8, &bytes, NULL)) //写入数据到设备
        return;
    
    ReadFile(hDev, recvDatatBuf, 8, &bytes, NULL); //读取设备发给主机的数据
}

HANDLE OpenMyHIDDevice(int overlapped)
{
    HANDLE     hidHandle;
    GUID     hidGuid;
    
    HidD_GetHidGuid(&hidGuid);
    
    HDEVINFO 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);
            devDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);

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

                  if (overlapped)
                  {
                         hidHandle = CreateFile(devDetail->DevicePath,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL, 
                        OPEN_EXISTING, 
                        FILE_FLAG_OVERLAPPED,
                        NULL);
                  }
                  else
                  {
                         hidHandle = CreateFile(devDetail->DevicePath,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL, 
                        OPEN_EXISTING, 
                        0,
                        NULL);
                  }

            free(devDetail);

            if (hidHandle==INVALID_HANDLE_VALUE)
            {
                SetupDiDestroyDeviceInfoList(hDevInfo);
                free(devDetail);
                return INVALID_HANDLE_VALUE;
            }

            _HIDD_ATTRIBUTES hidAttributes;
            if(!HidD_GetAttributes(hidHandle, &hidAttributes))
            {
                CloseHandle(hidHandle);
                           SetupDiDestroyDeviceInfoList(hDevInfo);
                return INVALID_HANDLE_VALUE;
            }

            if (USB_VID == hidAttributes.VendorID 
                && USB_PID == hidAttributes.ProductID)
            {
                break;
            }
            else
            {
                CloseHandle(hidHandle);
                ++deviceNo;
            }
        }
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
    return hidHandle;
}