[DllImport("Btdrt.dll", SetLastError=true)] public static extern int BthReadLocalAddr(byte[] pba); |
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")); |
INT WSALookupServiceBegin( LPWSAQUERYSET lpqsRestrictions, DWord dwControlFlags, LPHANDLE lphLookup ); |
[DllImport("ws2.dll", EntryPoint="WSALookupServiceBegin", SetLastError=true)] public static extern int CeLookupServiceBegin(byte[] pQuerySet, LookupFlags dwFlags, ref int lphLookup); |
The dwSize member must be sizeof(WSAQUERYSET). The lpBlob member (itself a pointer to a BLOB structure) is optional, but if used, the device 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 of the inquiry, in seconds. The dwNameSpace member must be NS_BTH. All other WSAQUERYSET members are ignored. |
byte[] buffer1 = new byte[0x400]; BitConverter.GetBytes(60).CopyTo(buffer1, 0); GCHandle handle1 = GCHandle.Alloc(blob1.ToByteArray(), GCHandleType.Pinned); IntPtr ptr1 = handle1.AddrOfPinnedObject(); BitConverter.GetBytes((int) (ptr1.ToInt32() + 4)).CopyTo(buffer1, 0x38); |
int num5 = BitConverter.ToInt32(buffer1, 0x30); 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里面的前六個字節是byte數組形式的設備ID,第七到第二十二個字節是藍牙的Service Guid,在后面四個字節是端口號,所以我們只需要分別提取出來即可。
四. 監聽服務
監聽服務調用的是非托管API WSASetService,其原型是INT WSASetService(
LPWSAQUERYSET lpqsRegInfo,
WSAESETSERVICEOP essOperation,
DWORD dwControlFlags
);
可以看到關鍵也是第一個參數,lpqsRegInfo,這也是一個struct,我們的包裝方法與前面的發現設備采用的方法類似,做藍牙通信時要注意其成員要如下設置:lpqsRegInfo dwSize 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. ipcsaBuffer Not supported on Windows CE. Set to 0. lpBlob Points to a BTHNS_SETBLOB structure, containing information about the service to be added. * All other WSAQUERYSET fields are ignored.
五. 連接
我們知道,IrDA中連接遠程服務是使用方法System.Net.Sockets.IrDAClient類中的Connect方法。而這個方法又是調用的Socket類中的Connect方法。而Socket類是一個比較抽象的類,它并不綁定某個具體的地址族、SocketType和PRotocolType,所以在實例化的時候,需要指定這三個參數。我們也知道,在IrDA中,這三個參數分別是AddressFamily.Irda, SocketType.Stream,和ProtocolType.IP,那么在藍牙中這三個參數分別是什么呢?我們好像找不到。
且慢,真是這樣嗎?
我們知道在.net中,這三個參數都是枚舉值,而枚舉在默認情況下,你可以認為就是int值的替代表現。
我們該如何知道這三個參數到底是什么呢?
還是先看Socket類的Connect方法。
我們查查有關資料,可以知道這個方法實際上是調用的一個非托管函數:[DllImport("mscoree", EntryPoint="@339")]
public static extern 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代表十六進制數0x0003,恰好和ProtocolType.Ggp代表的數值是一致的。所以,我們在實例化Socket時是這么寫的:new Socket((AddressFamily) 0x20, SocketType.Stream, ProtocolType.Ggp);
Socket實例化出來了,其他的當然就都好說了,這里不再贅述。
六. 藍牙的安全設置
藍牙比紅外多了安全方面的設置,所以就需要多一些代碼來處理這些。具體也就不多說了,其實也就是一些非托管代碼的包裝調用,這些API在Btdrt.dll中:
獲取配對碼請求:[DllImport("Btdrt.dll", SetLastError=true)]
public 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 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);
七. 設置藍牙無線電狀態
我們知道,藍牙無線電有打開、關閉、可發現三種狀態,那么我們如何實現編程控制呢?
我想這個一定大家都知道了,因為網上有很多關于這個的文章:
先寫一個枚舉:public enum RadioMode
{
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(ref RadioMode dwMode);
八. 已知的問題
可能是因為藍牙控制軟件還沒有實現標準化或者還是其他的問題,我們發現根據Windows CE 4.2 SDK 使用Winsock 擴展做的藍牙開發有一個問題,而且不論是本文中所述的托管代碼還是其他的非托管代碼,只要是用的這種思路用Winsock 2做的開發都會存在這樣一個問題,那就是不是在所有的Windows Mobile設備上都能正常運行。經過我的測試,我發現在很多使用另行開發的藍牙控制軟件的設備上,如聯想ET560、華碩MyPAL A730上都無法運行,而在沒有另行開發藍牙控制軟件的設備上是可以正常運行的,我不知道這是什么原因,初步推測可能是廠商另行開發的藍牙控制軟件屏蔽了微軟的API的緣故,到底是不是這樣,還得請高人指點。
新聞熱點
疑難解答