《2022年c#蓝牙开发 .pdf》由会员分享,可在线阅读,更多相关《2022年c#蓝牙开发 .pdf(7页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、c#蓝牙开发默认分类2009-10-13 17:05:47 阅读 305 评论 0 字号:大中小在 Windows Mobile软件开发中.net 正扮演着日益重要的角色,我们已经可以看到很多用.Net CF 开发的软件,这些软件涉及到了日常应用的方方面面。在智能设备的软件开发中,无线互联是一个相当重要的一块,我们可以看到,红外几乎是所有智能设备的标配,而蓝牙也日益在越来越多的智能设备上出现,有了硬件,显然要有相应的软件相关的应用。我们也知道,用.NET CF开发红外通信应用时相当轻松的,因为.NET CF中有一个命名空间System.Net.IrDA就是用于红外通信的通信模块。但是,.NET
2、 CF 中还没有关于蓝牙通信的模块,所以目前来讲做这方面的开发还有一定的困难。下面,就谈谈如何用C#开发.NET CF 蓝牙通信模块。一基本要点首先明确一点,因为涉及到驱动硬件的问题,所以仅靠了解C#开发的相关知识显然是无法完成开发的,我们必须对C+开发有所了解。但是为了简单起见,我们不希望用C+写半行代码,所有的编码工作全部使用 C#,也就是说,使用的开发环境只需要使用Visual S,不需要用其他的编辑器。作为开发这类驱动硬件的程序的知识准备,您需要了解C+的基本知识,知道头文件是怎么一回事,知道托管代码如何与非托管代码交互。因为本文的核心是说明如何开发.net CF 蓝牙通信模块,所以前
3、述这些准备知识并不作讲述。二关于蓝牙做蓝牙通信模块开发,自然先要知道蓝牙通信是怎么一回事。在我看来,蓝牙通信应该和红外通信模块类似,当然我是从开发者的角度来讲,抽象化以后应该就是这样,当然蓝牙和红外通信也有很多不一样的地方,这在面向对象设计里面怎么讲,我想一定有很多人理解的比我透彻。好了,这就是我们的基本思路了。我曾经在网上查过关于蓝牙开发的文章,很多人在.net CF 开发中把蓝牙通信当作一个串行通信来处理,这也是不错的,但是我不是很喜欢,因为这样做的话,并不是针对蓝牙来开发的,换言之,在使用过程中,需要先手动开启蓝牙,配对,连接,建立串行通道,然后开启应用程序使用,你还要在应用程序中设置串
4、行端口,对最终用户来讲,这是非常麻烦的。我觉得,这样的解决方案冠上蓝牙通信的名头简直就是 不多说了,书归正传。在红外通信中,我们知道,设备的 DeviceID是一个 Byte 数组,那么蓝牙设备的DeviceID什么样子呢?我想这个大家都很清楚,是一串以“:”分隔的 16 进制数字。红外通信中,一般而言红外并没有开启、关闭之类的状态,但是蓝牙有开启、关闭、可发现三种状态。红外没有安全设置,而蓝牙有安全设置,所以我们需要对蓝牙设备进行配对,而红外通信这部需要。我们查看.net 的 Socket 地址族里有IrDA,但是没有蓝牙相关的地址族,这是我们需要解决的问题。三获取设备 ID 名师资料总结-
5、精品资料欢迎下载-名师精心整理-第 1 页,共 7 页 -1获取本地设备的ID 我们查看 Window CE 4.2的 SDK 文档,得知获取本地设备ID 的函数是 BthReadLocalAddr,在 btdrt.dll中。SDK 文档中的英文原文是这样的:“This function retrieves the Bluetooth address of the current device.”好了,知道了这个就好说了:首先封装本地托管函数:DllImport(Btdrt.dll,SetLastError=true)public static extern int BthReadLocalA
6、ddr(byte PBa);这个函数得到的本地DeviceID 也是一组 byte 数组,为了向人们显示出来,我们要把它变为String:string text1=;text1=text1+pba 5.ToString(X2)+:;text1=text1+pba 4.ToString(X2)+:;text1=text1+pba 3.ToString(X2)+:;text1=text1+pba 2.ToString(X2)+:;text1=text1+pba 1.ToString(X2)+:;return(text1+pba 0.ToString(X2);2获取远程设备的ID 其实谈到获取远程设
7、备的ID 就涉及到如何去发现远程设备了,所以这里就一并把发现设备的方法也说明 了 吧。发 现 设 备 需 要 用 到 三 个Winsock API,分 别 是WSALookupServiceBegin、WSALookupServiceNext和 WSALookupServiceEnd,这三个 API 到底起什么作用可以去查看Windows CE 4.2 的 SDK,这里就不详细解释了,只谈一下几个需要注意的地方。WSALookupServiceBegin的函数原形是这样的:INT WSALookupServiceBegin(LPWSAQUERYSET lpqsRestrictions,DWOR
8、D dwControlFlags,名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 7 页 -LPHANDLE lphLookup);我们用托管代码进行包装:DllImport(ws2.dll,EntryPoint=WSALookupServiceBegin,SetLastError=true)public static extern int CeLookupServiceBegin(byte pQuerySet,LookupFlags dwFlags,ref int lphLookup);可以看到,本来lpqsRestrictions是一个 struct,经过包装后在托管代码中成
9、为了byte,我们计算好该 struct 大概要占用多少个byte,struct 中每一个成员在byte 数组中的位置是怎样的,装配出来就好了。由于是针对蓝牙作的开发,所以我们要查看一下这些参数应该是哪些值。Windows CE 4.2的 SDK 中说,蓝牙开发时,struct LPWSAQUERYSET中的如下成员应当为这些值:The dwSize member must be sizeof(WSAQUERYSET).The lpBlob member(itself a pointer to a BLOB structure)is optional,but if used,the devic
10、e inquire parameters valid for LUP_FLUSHCACHE are the following:The cbSize member of the BLOB structure must be sizeof(BTH_QUERY_DEVICE).The pBlobData member is a pointer to a BTH_QUERY_DEVICE structure,for which the LAP member is the Bluetooth inquiry Access code,and the length member is the length
11、 of the inquiry,in seconds.The dwNameSpace member must be NS_BTH.All other WSAQUERYSET members are ignored.具体什么意思各位可以自己去理解,我想比我翻译出来要好些,毕竟我英语很差的。根据以上要求,我们这样装配pQuerySet:byte buffer1=new byte0 x400;BitConverter.GetBytes(60).CopyTo(buffer1,0);GCHandle handle1=GCHandle.Alloc(blob1.ToByteArray(),GCHandleT
12、ype.Pinned);IntPtr ptr1=handle1.AddrOfPinnedObject();BitConverter.GetBytes(int)(ptr1.ToInt32()+4).CopyTo(buffer1,0 x38);名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 7 页 -另外的两个API 也照类似方法调用即可。在调用了 WSALookupServiceNext之后,bytes 数组 pQuerySet中便包含了远程设备的地址信息,下面我们需要把它找出来。通过阅读SDK 中 WSAQUERYSET结构的说明和计算每个成员的位置之后,我们写出如下代码:int
13、 num5=BitConverter.ToInt32(buffer1,0 x30);int num6=Marshal.ReadInt32(IntPtr)num5,8);int num7=Marshal.ReadInt32(IntPtr)num5,12);SocketAddress address1=new SocketAddress(AddressFamily.Unspecified,num7);因为.net 框架的地址族里面没有蓝牙,所以我们这里用的是AddressFamily.Unspecified。然后的工作就是从中获取远程设备的ID 了:前面我们已经计算出,这个Address里面的前六
14、个字节是byte 数组形式的设备ID,第七到第二十二个字节是蓝牙的Service Guid,在后面四个字节是端口号,所以我们只需要分别提取出来即可。四监听服务监听服务调用的是非托管API WSASetService,其原型是INT WSASetService(LPWSAQUERYSET lpqsRegInfo,WSAESETSERVICEOP essoperation,DWORD dwControlFlags);可以看到关键也是第一个参数,lpqsRegInfo,这也是一个struct,我们的包装方法与前面的发现设备采用的方法类似,做蓝牙通信时要注意其成员要如下设置:lpqsRegInfo d
15、wSize sizeof(WSAQUERYSET)lpszServiceInstanceName Not supported on Windows CE.Set to 0.lpServiceClassId Not supported on Windows CE.Set to 0.dwNameSpace NS_BTH.dwNumberOfCsAddrs Not supported on Windows CE.Set to 0.名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 7 页 -IpcsaBuffer Not supported on Windows CE.Set to 0.l
16、PBlob Points to a BTHNS_SETBLOB structure,containing information about the service to be added.*All other WSAQUERYSET fields are ignored.五连接我们知道,IrDA 中连接远程服务是使用方法S.Sockets.IrDAClient类中的 Connect方法。而这个方法又是调用的Socket 类中的 Connect 方法。而 Socket 类是一个比较抽象的类,它并不绑定某个具体的地址族、SocketType和 protocolType,所以在实例化的时候,需要指
17、定这三个参数。我们也知道,在 IrDA 中,这三个参数分别是AddressFamily.Irda,SocketType.Stream,和 ProtocolType.IP,那么在蓝牙中这三个参数分别是什么呢?我们好像找不到。且慢,真是这样吗?我们知道在.net 中,这三个参数都是枚举值,而枚举在默认情况下,你可以认为就是int 值的替代表现。我们该如何知道这三个参数到底是什么呢?还是先看 Socket 类的 Connect 方法。我们查查有关资料,可以知道这个方法实际上是调用的一个非托管函数:DllImport(mscoree,EntryPoint=339)public static exter
18、n int connect(int s,byte name,int namelen);也就是非托管的Socket API。我们看 Windows CE 4.2的 SDK,可以看到,在使用蓝牙进行连接的时候,需要使用WinSock 扩展。我们还可以看到,在使用蓝牙进行连接的时候,三个参数分别应当是AF_BTH、SOCK_STREAM和BTHPROTO_RFCOMM,至于这三个参数分别代表什么,我们就要查看相关的头文件了。我们找到 ws2bth.h头文件,可以看到AF_BTH 代表十进制数32,而 BTHPROTO_RFCOMM代表十六进制数0 x0003,恰好和ProtocolType.Ggp代
19、表的数值是一致的。所以,我们在实例化Socket 时是这么写的:new Socket(AddressFamily)0 x20,SocketType.Stream,ProtocolType.Ggp);Socket 实例化出来了,其他的当然就都好说了,这里不再赘述。六蓝牙的安全设置名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 7 页 -蓝牙比红外多了安全方面的设置,所以就需要多一些代码来处理这些。具体也就不多说了,其实也就是一些非托管代码的包装调用,这些API 在 Btdrt.dll中:获取配对码请求:DllImport(Btdrt.dll,SetLastError=true)pu
20、blic static extern int BthGetPINRequest(byte pba);设置配对码:DllImport(btdrt.dll,SetLastError=true)public static extern int BthSetPIN(byte pba,int cPinLength,byte ppin);比较麻烦点的是配对,总共有三步操作:首先是创建ACL 连接:DllImport(Btdrt.dll,SetLastError=true)public static extern int BthCreateACLConnection(byte pbt,ref ushort
21、phandle);然后是配对码验证:DllImport(Btdrt.dll,SetLastError=true)public static extern int BthAuthenticate(byte pbt);然后一定要关闭连接:DllImport(Btdrt.dll,SetLastError=true)public static extern int BthCloseConnection(ushort handle);七设置蓝牙无线电状态我们知道,蓝牙无线电有打开、关闭、可发现三种状态,那么我们如何实现编程控制呢?我想这个一定大家都知道了,因为网上有很多关于这个的文章:先写一个枚举:pu
22、blic enum RadioMode 名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 7 页 -Connectable=1,Discoverable=2,PowerOff=0 然后写一个函数调用非托管代码即可:DllImport(BthUtil.dll,SetLastError=true)public static extern int BthSetMode(RadioMode dwMode);获取无线电状态的话就用下面的函数:DllImport(BthUtil.dll,SetLastError=true)public static extern int BthGetMode(
23、ref RadioMode dwMode);八已知的问题可能是因为蓝牙控制软件还没有实现标准化或者还是其他的问题,我们发现根据Windows CE 4.2 SDK 使用 Winsock 扩展做的蓝牙开发有一个问题,而且不论是本文中所述的托管代码还是其他的非托管代码,只要是用的这种思路用Winsock 2做的开发都会存在这样一个问题,那就是不是在所有的Windows Mobile 设备上都能正常运行。经过我的测试,我发现在很多使用另行开发的蓝牙控制软件的设备上,如联想 ET560、华硕 MyPAL A730上都无法运行,而在没有另行开发蓝牙控制软件的设备上是可以正常运行的,我不知道这是什么原因,初步推测可能是厂商另行开发的蓝牙控制软件屏蔽了微软的API 的缘故,到底是不是这样,还得请高人指点。名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 7 页 -