Introduction
This code sample is named WMBluetoothWrapper. It demonstrates simple Bluetooth device discovery via the Winsock 2 API, and provides a C# wrapper class for running the discovery. Both the names of the mobile devices and their addresses are retrieved and marshaled to C# data types. I provide both the Visual Studio 2010 DLL project WMBluetoothWrapper which present the WinSock 2 based discovery function as well as the C# file including the DevicesDiscoveryWrapper
wrapper class. Using the retrieved addresses, the current wrapper can be extended to include further functions, such as Bluetooth based connection and data exchanging functions.
Background
As part of my plan to write a mobile social network application (coming soon) using Windows Mobile Bluetooth capable devices, I need a way to know what Bluetooth devices are near me. I'm developing a mobile social network application in C# using the .NET Compact Framework 3.5, and thus I was confronted with the need of writing the whole low level Bluetooth based functionalities in C++, compiling them into a DLL, and finally writing a C# wrapper class and using P/Invoke(s) in order to run the Bluetooth functions. Before going ahead with the development of the wrapper, I've tried to find third-party libraries in C# exposing Bluetooth discovery functionalities, and I found 32feet.NET which seems to be popular from what I've read; however, it seems to present some license limitations, and after all it is better to have the control on a simpler and customizable wrapper.
Architecture
The DevicesDiscoveryWrapper
class is written in C#, and it uses P/Invoke to gain access to exported functions within the WMBluetoothWrapper DLL file. The latter is written in C++, and makes use of Bluetooth functionalities provided by the Winsock 2 API in order to build the devices discovery function.
Using the Code
In order to use the Bluetooth devices discovery wrapper when developing your application, you will just add the C# file DevicesDiecoveryWrapper.cs to your project and use the class DevicesDiscoveryWrapper
. For deployment, you should not forget to copy the DLL file WMBluetoothWrapper.dll into your project mobile working directory. Below, I provide a detailed description of both the devices discovery function written using the Bluetooth Winsock 2 API and the C# wrapper class.
The WMBluetoothWrapper DLL Code
WMBluetoothWrapper.h:
extern "C" int DevicesDiscovery(wchar_t * devicesList);
WMBluetoothWrapper.cpp:
extern "C" int DevicesDiscovery(wchar_t * devicesList)
{
WSADATA wsd;
WSAStartup (MAKEWORD(1,0), &wsd);
HANDLE hLookup;
union {
CHAR buf[5000];
double __unused; };
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;
}
The DevicesDiscoveryWrapper Wrapper Class
Below you find the code for the wrapper class developed in C#. A common scenario for using this wrapper would be to periodically start a detached thread and to specify the RunDevicesDiscovery
method as its start point. Then, the managed thread will run a P/Invoke and call the unmanaged devices discovery function provided by the DLL file. Once done, the thread modifies the devicesList
and numberDevices
according to the found results. So, the user can retrieve the number and the list of available devices. I've also used the Singleton Design Pattern within the wrapper class in order to ensure that it will be instantiated only once.
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;
}
}
}
}
Tools of Interest
The DLL Export Viewer tool was of great help to me for verifying/debugging the list of exported functions and their virtual memory addresses for the WMBluetoothWrapper DLL file. More details about this tool can be found at: http://www.nirsoft.net/utils/dll_export_viewer.html.
Contact
For bug reports and suggestions, feel free to contact me at krifa.amir@gmail.com.
History
- 18th March, 2010: Initial post.