在开发移动社交网络应用时,了解附近可用的蓝牙设备是非常重要的。本文将介绍如何使用Winsock 2 API实现蓝牙设备发现功能,并提供一个C#封装类来简化这一过程。
为了开发一个使用Windows Mobile蓝牙功能的移动社交网络应用,需要一种方法来识别附近的蓝牙设备。正在使用.NET Compact Framework 3.5和C#开发这个应用,这意味着需要用C++编写底层的蓝牙功能,将它们编译成DLL,然后编写一个C#封装类并通过P/Invoke来调用这些蓝牙功能。在开发封装类之前,尝试寻找了C#中提供蓝牙发现功能的第三方库,发现了32feet.NET,但它有一些许可限制。因此,决定自己编写一个更简单、可定制的封装类。
封装类DevicesDiscoveryWrapper
是用C#编写的,它使用P/Invoke来访问WMBluetoothWrapper
DLL文件中导出的函数。后者是用C++编写的,利用Winsock 2 API提供的蓝牙功能来构建设备发现功能。
要在开发应用时使用蓝牙设备发现封装类,只需将C#文件DevicesDiscoveryWrapper.cs
添加到项目中,并使用DevicesDiscoveryWrapper
类。部署时,不要忘记将DLL文件WMBluetoothWrapper.dll
复制到项目移动工作目录中。下面,将详细描述使用蓝牙Winsock 2 API编写的设备发现功能和C#封装类。
以下是C++代码示例,展示了如何实现设备发现功能:
// WMBluetoothWrapper.h
extern "C" int DevicesDiscovery(wchar_t* devicesList);
// WMBluetoothWrapper.cpp
#include
extern "C" int DevicesDiscovery(wchar_t* devicesList) {
WSADATA wsd;
WSAStartup(MAKEWORD(1, 0), &wsd);
HANDLE hLookup;
union {
CHAR buf[5000];
double __unused; // ensure proper alignment
};
LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET)buf;
DWORD dwSize = sizeof(buf);
BOOL bHaveName;
WSAQUERYSET wsaq;
ZeroMemory(&wsaq, sizeof(wsaq));
wsaq.dwSize = sizeof(wsaq);
wsaq.dwNameSpace = NS_BTH;
wsaq.lpcsaBuffer = NULL;
if (ERROR_SUCCESS != WSALookupServiceBegin(&wsaq, LUP_CONTAINERS, &hLookup)) {
wsprintf(devicesList, L"WSALookupServiceBegin failed %d\r\n", GetLastError());
return -1;
}
ZeroMemory(pwsaResults, sizeof(WSAQUERYSET));
pwsaResults->dwSize = sizeof(WSAQUERYSET);
pwsaResults->dwNameSpace = NS_BTH;
pwsaResults->lpBlob = NULL;
int numberRetrivedDevices = 0;
while (ERROR_SUCCESS == WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults)) {
wchar_t currentDev[1024];
if (pwsaResults->dwNumberOfCsAddrs != 1) {
wsprintf(devicesList, L"WSALookupServiceNext failed %d\r\n", GetLastError());
return -1;
}
if (numberRetrivedDevices > 0)
wcscat(devicesList, L",");
BT_ADDR b = ((SOCKADDR_BTH *)pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr;
bHaveName = pwsaResults->lpszServiceInstanceName && *(pwsaResults->lpszServiceInstanceName);
wsprintf(currentDev, L"%s%s%04x%08x%s\n", bHaveName ? pwsaResults->lpszServiceInstanceName : L"", bHaveName ? L"(" : L"", GET_NAP(b), GET_SAP(b), bHaveName ? L")" : L"");
wcscat(devicesList, currentDev);
numberRetrivedDevices++;
}
WSALookupServiceEnd(hLookup);
WSACleanup();
return numberRetrivedDevices;
}
以下是C#封装类代码示例:
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
class DevicesDiscoveryWrapper {
[DllImport("WMBluetoothWrapper.dll")]
private static extern System.Int32 DevicesDiscovery([MarshalAs(UnmanagedType.LPWStr)] StringBuilder listDevices);
private static string devicesList;
private static int numberDevices;
private static DevicesDiscoveryWrapper _wrapper = null;
private DevicesDiscoveryWrapper() {
devicesList = null;
numberDevices = 0;
}
public static DevicesDiscoveryWrapper GetDevicesDiscoveryWrapper() {
if (_wrapper == null) {
_wrapper = new DevicesDiscoveryWrapper();
}
return _wrapper;
}
public string GetDevices {
get {
lock (this) {
return devicesList;
}
}
}
public int GetNumberOfDevices {
get {
lock (this) {
return numberDevices;
}
}
}
public void RunDevicesDiscovery() {
lock (this) {
StringBuilder tmpListDevices = new StringBuilder();
tmpListDevices.Capacity = 1024;
int res = DevicesDiscovery(tmpListDevices);
if (res == -1) {
MessageBox.Show("Error occurred while calling the unmanaged DevicesDiscovery functions: " + tmpListDevices.ToString());
} else {
devicesList = tmpListDevices.ToString();
numberDevices = res;
}
}
}
}
DLL Export Viewer工具对于验证/调试WMBluetoothWrapper
DLL文件中导出的函数列表及其虚拟内存地址非常有用。有关此工具的更多详细信息,请访问:。