See http://32feet.codeplex.com/releases/view/67702 ūüôā

A page from the BlueCove Java Bluetooth library documentation showing how to identify which Bluetooth stack is installed http://www.bluecove.org/bluecove-examples/bluetooth-stack.html It shows screenshots of the device driver info on Windows XP for the Microsoft, Widcomm, and BlueSoleil stacks.

I’ve just checked in support for Widcomm’s COM Port creation classes. Access WidcommSerialPort.CreateClient and store the result and once you’ve finished with the COM port Dispose it, it’ll be Finalized if not referenced.¬† I’ll look at integrating the functionality into the¬†BluetoothSerialPort class sometime.

I’ve tested it on my Asus WM Classic device and it work fine there. The Widcomm documentation implies that it should work on Win32 too but it doesn’t work on my WinXP Widcomm v3 installation.

Download the library code (revision 85315 or later) from http://32feet.codeplex.com/SourceControl/list/changesets and compile the CF2 project to get the library code to use in your project. You’ll also need the new native Widcomm mapping DLL, get it from http://32feet.codeplex.com/releases/view/61443

Testing shows that if the connected devices go out of range then the connection is lost however (we see DISCONNECT event in the code, and the remote socket server sees close). Presumably we need to implement code to repeatedly retry to reconnect?

I’ve done about a day’s work on that. I’d be glad of a wee ‘reward’ for adding this new feature, particularly if it saves you time on your projects. ūüôā I’ve Amazon wishlist or paypal for instance.

Mostly as an aid memoire to myself, to help me remember how to find the version of radio (‚Äúcontroller‚ÄĚ in official terminology IIRC) in the various Bluetooth stacks.

Microsoft on Windows XP, Windows 7 etc

Windows 7 Advanced tab of the Bluetooth Control Panel

Admin view of Windows XP Advanced tab of the Bluetooth Control Panel No version on Windows XP's non-admin view...

No version on Windows XP's non-admin view...


Widcomm (Windows XP at least) shows the version on the Hardware tab of its Control Panel.


BlueSoleil (Windows 7 at least) shows the version on the Hardware tab of its Control Panel.

BlueZ on Linux

Not from hcitool:

alan@foobar:~/> hcitool dev
 hci0 00:15:83:B4:1B:FA
 hci1 00:80:98:24:4C:A4

Yes from root hciconfig:

foobar:/home/alan # hciconfig hci0 version
hci0: Type: BR/EDR  Bus: USB
 BD Address: 00:15:83:B4:1B:FA  ACL MTU: 384:8  SCO MTU: 64:8
 HCI Version: 2.0 (0x3)  Revision: 0x7a6
 LMP Version: 2.0 (0x3)  Subversion: 0x7a6
 Manufacturer: Cambridge Silicon Radio (10)
foobar:/home/alan # hciconfig hci1 version
hci1: Type: BR/EDR  Bus: USB
 BD Address: 00:80:98:24:4C:A4  ACL MTU: 192:8  SCO MTU: 64:8
 HCI Version: 1.1 (0x1)  Revision: 0x20c
 LMP Version: 1.1 (0x1)  Subversion: 0x20c
 Manufacturer: Cambridge Silicon Radio (10)

foobar:/home/alan # hciconfig -a
hci0: Type: BR/EDR  Bus: USB
 BD Address: 00:15:83:B4:1B:FA  ACL MTU: 384:8  SCO MTU: 64:8
 RX bytes:1677 acl:0 sco:0 events:41 errors:0
 TX bytes:498 acl:0 sco:0 commands:41 errors:0
 Features: 0xff 0xff 0x8f 0xfe 0x9b 0xf9 0x00 0x80
 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
 Link mode: SLAVE ACCEPT
 Name: 'linux-0'
 Class: 0x4a0100
 Service Classes: Networking, Capturing, Telephony
 Device Class: Computer, Uncategorized
 HCI Version: 2.0 (0x3)  Revision: 0x7a6
 LMP Version: 2.0 (0x3)  Subversion: 0x7a6
 Manufacturer: Cambridge Silicon Radio (10)

hci1: Type: BR/EDR  Bus: USB
 BD Address: 00:80:98:24:4C:A4  ACL MTU: 192:8  SCO MTU: 64:8
 RX bytes:689 acl:0 sco:0 events:22 errors:0
 TX bytes:86 acl:0 sco:0 commands:22 errors:0
 Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00
 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
 Link policy:
 Link mode: SLAVE ACCEPT
 Name: 'ALANPC1a'
 Class: 0x4a0000
 Service Classes: Networking, Capturing, Telephony
 Device Class: Miscellaneous,
 HCI Version: 1.1 (0x1)  Revision: 0x20c
 LMP Version: 1.1 (0x1)  Subversion: 0x20c
 Manufacturer: Cambridge Silicon Radio (10)

foobar:/home/alan #

Version Numbers

The X.X e.g 1.1, 3.0, version numbers are actually represented by a simple integer, HCI and LMP strictly don’t use the same definitions but the two use the same numbering system currently and likely will do forever.  The current definitions are:

0 Bluetooth Core Specification 1.0b
1 Bluetooth Core Specification 1.1
2 Bluetooth Core Specification 1.2
3 Bluetooth Core Specification 2.0 + EDR
4 Bluetooth Core Specification 2.1 + EDR
5 Bluetooth Core Specification 3.0 + HS
6 Bluetooth Core Specification 4.0
7 ‚Äď 255 Reserved

The definitions are available at https://www.bluetooth.org/Technical/AssignedNumbers/hci.htm¬†and https://www.bluetooth.org/Technical/AssignedNumbers/link_manager.htm¬†The Bluetooth specification documents have moved, unfortunately there’s a fault on the HCI document which requires Bluetooth log-in to access.¬† (http://bluetooth.com/Specification%20Documents/AssignedNumbersHostControllerInterface1.pdf and ¬†http://bluetooth.com/Specification%20Documents/AssignedNumbersLinkManager1.pdf)

Available from http://32feet.codeplex.com/releases/view/39346

Release Notes

The code is stable, the libraries are Strong-Named, but the Help is not integrated into Visual Studio’s Help.

The main features and bug fixes in this release are as follows. For Bluetooth:
‚ÄĘ FIX BluetoothRadio.LocalAddress is all zeros in v2.5 on WM+MSFT (bug 26123) ‚ÄĒ as broken at version 2.5
‚ÄĘ Live discovery — prototype for Widcomm and WM+MSFT (bug 28491) ‚ÄĒ see the User Guide for more information
‚ÄĘ Support setting BluetoothRadio.Name on Win32+MSFT (bug 26149)
‚ÄĘ SDP support for 64-bit integers and parsing 128-bit integers (bug 26118, 26119, 28884)

For Bluetooth on Widcomm:
‚ÄĘ Support BtRadio.set_Mode (can CE/WM only) (bug 26239)
‚ÄĘ BtRadio.Mode reports wrong values when radio has been turned off (bug 26240) ‚ÄĒ this requires a recent versions of the Widcomm stack
‚ÄĘ Fully Widcomm threading compatible including on CE/WM ‚ÄĒ this is for instance visible to the developer in that Connect can now be called from EndDiscoverDevices etc (bug 26186, 26194, 26195, 26868)
‚ÄĘ Close connections on app crash etc (bug 28628)
‚ÄĘ Use OnStackStatusChange to close connections when radio turned off (bug 28623)

There are no OBEX or IrDA changes in this release.

For Widcomm, various users have reported that there are problems on desktop Windows with newer versions of the Widcomm stack, with for instance BluetoothClient.Connect failing with a SocketException with it message including the code ‚ÄúPortLookup_NoneRfcomm‚ÄĚ. We now supply two versions of 32feetWidcomm.dll for Win32 for this reason. Unfortunately when to use them is complex‚ÄĒ I wish Widcomm had been a bit cleverer about how they provided their Vista support. ūüė¶ (Note that Bluecove on Java also has to supply two versions of the DLL presumably for similar reasons ).

  • Normal 32feetWidcomm.dll version: Works even when the Microsoft Bluetooth stack is also active, and so allows multi-stack support. But might not work on newer version installations of the Widcomm stack.
  • ‚ÄúSDK6‚ÄĚ version: May be required on newer version installations of the Widcomm stack, but will not work when the Microsoft Bluetooth stack is active.

We also now include copies of the 32feetWidcomm.dll for x64, let me know if it works for you, it is untested by us.

—- Preliminary, needs tidying —-

Users coming to .NET from other platforms are often surprised that they have to do blocking reads on a Stream or Socket to read data from the network. And when they asks how to stop blocking the UI, the normal answer is to tell the them either to move the stream/socket reading onto a background thread, or to use the APM (asynchronous programming model) with the BeginRead/EndRead methods…

Which is ok until the programmer soon asks what “cross-thread” exceptions are. ¬†Since both of those schemes have the data-arrival code run on a different thread than the UI we then have to explain all the complexity of cross-thread marshalling, whether using Control.BeginInvoke on WinForms, or the more general SyncronizationContext use.

So why don’t we just have the data arrival signalled by event, that’s how VB did it, and automatically ensure that the event runs on the UI thread. So the user simply signs-up for the respective events and all his/her problems are solved: no blocking, and no cross-thread marshalling required.

Until such a class is provided in the FCL (framework class library) the following will have to do. ūüôā
Add this into a new file in your project (converting to C# if necessary). Then subscribe to the events, set it running on the connection and all should be good.

' Copyright (c) 2009 Alan J. McFarlane
' Free for any use. No rights reserved.

Option Explicit On
Option Strict On
Imports System
Imports System.IO
Imports System.Threading

''' <summary>
''' Raises events when data is received on a <see cref="T:System.IO.Stream" />
''' </summary>
''' <remarks>
''' <para>Raises three events <see cref="E:DataReceived" />, <see cref="E:ConnectionClosed" />,
''' and <see cref="E:ErrorOccurred" />.
''' </para>
''' <para>The events are called on the correct synchronization context, e.g. on
''' the UI thread in a WinForms app.
''' </para>
''' </remarks>
''' -
''' <example>
''' <code>
''' ...
''' Dim strm As Stream = cli.GetStream() ' etc...
''' Dim efs As New EventFromStream() ' We pick-up the UI thread when initialised
''' AddHandler efs.DataReceived, AddressOf HandleDataReceived
''' AddHandler efs.ErrorOccurred, AddressOf HandleErrorOccurred
''' AddHandler efs.ConnectionClosed, AddressOf HandleConnectionClosed
''' efs.Run(strm)
''' ...
''' End Sub
''' Sub HandleDataReceived(ByVal sender As Object, ByVal e as DataReceivedEventArgs)
''' Dim data() As Byte = e.Data
''' ...
''' End Sub
''' ...
''' </code>
''' </example>
Class EventFromStream : Inherits System.ComponentModel.Component
Private m_strm As Stream
Private m_syncCtx As SynchronizationContext
Private m_disposed As Integer
Private m_disposedEvent As New ManualResetEvent(False)

Friend Sub New()
m_syncCtx = SynchronizationContext.Current
End Sub

Overloads Sub Dispose(ByVal disposing As Boolean)
Thread.VolatileWrite(m_disposed, 1)
' Should we close the Stream? It makes life much easier for us if
' we do but
End Try
End Sub

Private ReadOnly Property IsDisposed() As Boolean
Dim v As Integer = Thread.VolatileRead(m_disposed)
Dim disposed As Boolean = v <> 0
Return disposed
End Get
End Property

Public Event DataReceived As EventHandler(Of DataReceivedEventArgs)

Public Event ConnectionClosed As EventHandler(Of EventArgs)

Public Event ErrorOccurred As EventHandler(Of ErrorEventArgs)

Public Sub Run(ByVal strm As Stream)
If m_strm IsNot Nothing Then Throw New ArgumentException("Supports one stream at the moment")
m_strm = strm
ThreadPool.QueueUserWorkItem(AddressOf Runner)
End Sub

Private Sub Runner(ByVal state As Object)
Dim buf(1024 - 1) As Byte
While True
Dim readLen As Integer
#If False Then
readLen = m_strm.Read(buf, 0, buf.Length)
' Use the async version so we can exit when disposed
Dim ar As IAsyncResult = m_strm.BeginRead(buf, 0, buf.Length, Nothing, Nothing)
Dim ah() As WaitHandle = {ar.AsyncWaitHandle, m_disposedEvent}
Dim signalled As Integer = WaitHandle.WaitAny(ah, -1)
If signalled = 0 Then
readLen = m_strm.EndRead(ar)
' We leave that BeginReceive hanging in this case. :-( There's
' not much we can do about that (well we could start a thread
' sitting on EndRead...).
' It would be much nicer if we closed the stream when we were
' disposed...
Exit While
End If
#End If
Catch ex As Exception
' Probably due to the stream closing, so ignore.
If IsDisposed Then Exit While
Exit While
End Try
If readLen = 0 Then
Exit While
End If
DoDataReceived(Clone(buf, 0, readLen))
End While
End Sub

Private Sub DoError(ByVal ex As Exception)
m_syncCtx.Send(AddressOf DoErrorS, ex)
End Sub

Private Sub DoErrorS(ByVal state As Object)
Dim ex As Exception = CType(state, Exception)
RaiseEvent ErrorOccurred(Me, New ErrorEventArgs(ex))
End Sub

Private Sub DoConnectionClosed()
m_syncCtx.Send(AddressOf DoConnectionClosedS, Nothing)
End Sub

Private Sub DoConnectionClosedS(ByVal state As Object)
RaiseEvent ConnectionClosed(Me, New EventArgs)
End Sub

Private Sub DoDataReceived(ByVal data() As Byte)
m_syncCtx.Send(AddressOf DoDataReceivedS, data)
End Sub

Private Sub DoDataReceivedS(ByVal state As Object)
Dim data() As Byte = CType(state, Byte())
RaiseEvent DataReceived(Me, New DataReceivedEventArgs(data))
End Sub

Private Function Clone(ByVal buf As Byte(), ByVal offset As Integer, ByVal length As Integer) As Byte()
' Create a new array of the correct size
Dim buf2() As Byte = CType(Array.CreateInstance(GetType(Byte), length), Byte())
Array.Copy(buf, offset, buf2, 0, length)
Return buf2
End Function
End Class

Class DataReceivedEventArgs : Inherits EventArgs
Private m_data() As Byte

Friend Sub New(ByVal data() As Byte)
m_data = data
End Sub

Public ReadOnly Property Data() As Byte()
Return m_data
End Get
End Property
End Class

Currently the codeplex¬†repository has the namespace folders arranged in a hierarchical fashion, i.e. Net/*.cs, Net/Bluetooth/*.cs, Net/Bluetooth/Sdp2/*.cs etc.¬† This is rather unwieldy to work with, for instance if one wants to see files in Net/Bluetooth/Sdp2/ one has to have both it and the two parent¬†folder open which takes up lots of space with the files from all three.¬† I intend to change this to be a simple list of folders at the top level: Net/, Net/Bluetooth/, Net/Bluetooth/Sdp2, … , Net/Socket/ etc.

This might affect¬†people who use a source control client to keep in sync with the code (e.g. the VS Team System client (is this the correct name?), or an SVN¬†client for instance).¬† People who do a complete download using the ZIP file download shouldn’t be affected.¬† Hopefully the source-code-control clients recognise a “folder rename” operation and just move the files in the folder locally instead of having to re-download them.¬† Also things might behave worse if there’s a local edit to a file the moves folder.¬† So I’ll hold off from making any major changes until Friday at the earliest.

I’ve made two changes already to test how things behave.¬† I moved Net/IrDA/ and Net/Mime/.¬† In the latter test I made a local edit in one (team-system) repository and carried out the folder renaming in another repo.¬† Then on updating the first repository the following sequence of operations occurred:

  1. A dialog box reporting ‘ could not delete non-empty folder “Mime”‘ or words to that effect.¬† I just clicked the OK button.
  2. The conflicts dialog was then displayed and I clicked “Resolve all automatically”.¬† I was then prompted about the ‘moved and edited’ file to which I asked it to merge the local changes. ¬†It then asked whether to keep the local name or the server name, I chose the latter.
  3. After all that everything looked ok.  The file was in the correct folder and with my local change present.  (The old Net/Mime/ folder was still present as implied by #1, but it was of course empty and I deleted it manually for tidyness.

It wasn’t clear in these cases whether the moved files were re-downloaded or just shifted locally.

Let me know if this will cause any great difficulties.

Next Page »