The Microsoft Bluetooth stack on both desktop Windows and WM/CE uses sockets for RFCOMM communications, and they use socket options for both setting parameters and getting information.  We use two of the options to implement the BluetoothClient and BluetoothListener Authenticate and Encrypt properties, and another on WM/CE for the SetPin method.

As these options are present only on the Microsoft platforms and even vary between the two platforms they aren’t very useful for production use, but they can be useful for diagnostics etc.  We recently had a question in the forums and I knocked up a bit of code to run through all the options we’ve seen in MSDN and see what values they return.  The code to run through all the known option is in our code repository at codeplex in the XxxMenuTesting suite of apps (Forms-/Device-/ConsoleMenuTesting) and uses reflection to get the list of options.  I won’t reproduce that here due to its complexity, but a sample simple(!) call would be:

    System.Net.Sockets.Socket sock = ... ...;
    System.Net.Sockets.SocketOptionName option;
#if NETCF
    option = BluetoothSocketOptionName.GetMtu;
#else
    option = BluetoothSocketOptionName.XPMtu;
#endif
    var v = sock.GetSocketOption(BluetoothSocketOptionLevel.RFComm, option);
    var i = (int)v;
    console.WriteLine("Option MTU value: {0}=0x{0:X}", i);

Firstly the result from a WM device, when connected to Widcomm on Windows XP. We get fifteen successes there.

---- TryAllMsftSocketOptions ----
Option:             Authenticate           1 : error:  (10042)
Option:           XPAuthenticate  0x80000001 : error:  (10042)
Option:                  Encrypt           2 : error:  (10042)
Option:                   SetPin           3 : error:  (10042)
Option:                  SetLink           4 : error:  (10042)
Option:                  GetLink           5 : error:  (10022)
Option:                    XPMtu  0x80000007 : error:  (10042)
Option:                   SetMtu           6 : error:  (10042)
Option:                   GetMtu           7 : value: 127=0x7F
Option:             XPMtuMaximum  0x80000008 : error:  (10042)
Option:            SetMtuMaximum           8 : error:  (10042)
Option:            GetMtuMaximum           9 : value: 32767=0x7FFF
Option:             XPMtuMinimum  0x8000000A : error:  (10042)
Option:            SetMtuMinimum          10 : error:  (10042)
Option:            GetMtuMinimum          11 : value: 23=0x17
Option:              SetXOnLimit          12 : error:  (10042)
Option:              GetXOnLimit          13 : value: 800=0x320
Option:             SetXOffLimit          14 : error:  (10042)
Option:             GetXOffLimit          15 : value: 200=0xC8
Option:            SetSendBuffer          16 : error:  (10042)
Option:            GetSendBuffer          17 : value: 7168=0x1C00
Option:         SetReceiveBuffer          18 : error:  (10042)
Option:         GetReceiveBuffer          19 : value: 7168=0x1C00
Option:              GetV24Break          20 : error:  (10022)
Option:                   GetRls          21 : value: 0=0x0
Option:                  SendMsc          22 : error:  (10042)
Option:                  SendRls          23 : error:  (10042)
Option:              GetFlowType          24 : value: 1=0x1
Option:           SetPageTimeout          25 : error:  (10042)
Option:           GetPageTimeout          26 : value: 9472=0x2500
Option:                  SetScan          27 : error:  (10042)
Option:                  GetScan          28 : value: 3=0x3
Option:                   SetCod          29 : error:  (10042)
Option:                   GetCod          30 : value: 5898764=0x5A020C
Option:          GetLocalVersion          31 : error:  (10022)
Option:         GetRemoteVersion          32 : error:  (10022)
Option: GetAuthenticationEnabled          33 : value: 0=0x0
Option: SetAuthenticationEnabled          34 : error:  (10042)
Option:           ReadRemoteName          35 : error:  (10042)
Option:            GetLinkPolicy          36 : value: 15=0xF
Option:            SetLinkPolicy          37 : error:  (10042)
Option:            EnterHoldMode          38 : error:  (10042)
Option:           EnterSniffMode          39 : error:  (10042)
Option:            ExitSniffMode          40 : error:  (10042)
Option:            EnterParkMode          41 : error:  (10042)
Option:             ExitParkMode          42 : error:  (10042)
Option:                  GetMode          43 : value: 0=0x0
---- CLEAN EXIT ----

Secondly on Windows XP, when connected to a MSFT+WM6, we only get one successful on XP.

---- TryAllMsftSocketOptions ----
Option:             Authenticate           1 : error:  (10042 ProtocolOption)
Option:           XPAuthenticate  0x80000001 : error:  (10042 ProtocolOption)
Option:                  Encrypt           2 : error:  (10042 ProtocolOption)
Option:                   SetPin           3 : error:  (10042 ProtocolOption)
Option:                  SetLink           4 : error:  (10042 ProtocolOption)
Option:                  GetLink           5 : error:  (10042 ProtocolOption)
Option:                    XPMtu  0x80000007 : value: 666=0x29A
Option:                   SetMtu           6 : error:  (10042 ProtocolOption)
Option:                   GetMtu           7 : error:  (10042 ProtocolOption)
Option:             XPMtuMaximum  0x80000008 : error:  (10042 ProtocolOption)
Option:            SetMtuMaximum           8 : error:  (10042 ProtocolOption)
Option:            GetMtuMaximum           9 : error:  (10042 ProtocolOption)
Option:             XPMtuMinimum  0x8000000A : error:  (10042 ProtocolOption)
Option:            SetMtuMinimum          10 : error:  (10042 ProtocolOption)
Option:            GetMtuMinimum          11 : error:  (10042 ProtocolOption)
Option:              SetXOnLimit          12 : error:  (10042 ProtocolOption)
Option:              GetXOnLimit          13 : error:  (10042 ProtocolOption)
Option:             SetXOffLimit          14 : error:  (10042 ProtocolOption)
Option:             GetXOffLimit          15 : error:  (10042 ProtocolOption)
Option:            SetSendBuffer          16 : error:  (10042 ProtocolOption)
Option:            GetSendBuffer          17 : error:  (10042 ProtocolOption)
Option:         SetReceiveBuffer          18 : error:  (10042 ProtocolOption)
Option:         GetReceiveBuffer          19 : error:  (10042 ProtocolOption)
Option:              GetV24Break          20 : error:  (10042 ProtocolOption)
Option:                   GetRls          21 : error:  (10042 ProtocolOption)
Option:                  SendMsc          22 : error:  (10042 ProtocolOption)
Option:                  SendRls          23 : error:  (10042 ProtocolOption)
Option:              GetFlowType          24 : error:  (10042 ProtocolOption)
Option:           SetPageTimeout          25 : error:  (10042 ProtocolOption)
Option:           GetPageTimeout          26 : error:  (10042 ProtocolOption)
Option:                  SetScan          27 : error:  (10042 ProtocolOption)
Option:                  GetScan          28 : error:  (10042 ProtocolOption)
Option:                   SetCod          29 : error:  (10042 ProtocolOption)
Option:                   GetCod          30 : error:  (10042 ProtocolOption)
Option:          GetLocalVersion          31 : error:  (10042 ProtocolOption)
Option:         GetRemoteVersion          32 : error:  (10042 ProtocolOption)
Option: GetAuthenticationEnabled          33 : error:  (10042 ProtocolOption)
Option: SetAuthenticationEnabled          34 : error:  (10042 ProtocolOption)
Option:           ReadRemoteName          35 : error:  (10042 ProtocolOption)
Option:            GetLinkPolicy          36 : error:  (10042 ProtocolOption)
Option:            SetLinkPolicy          37 : error:  (10042 ProtocolOption)
Option:            EnterHoldMode          38 : error:  (10042 ProtocolOption)
Option:           EnterSniffMode          39 : error:  (10042 ProtocolOption)
Option:            ExitSniffMode          40 : error:  (10042 ProtocolOption)
Option:            EnterParkMode          41 : error:  (10042 ProtocolOption)
Option:             ExitParkMode          42 : error:  (10042 ProtocolOption)
Option:                  GetMode          43 : error:  (10042 ProtocolOption)
---- CLEAN EXIT ----

* WM
“setsockopt (Bluetooth)” http://msdn.microsoft.com/en-us/library/aa915899.aspx
“getsockopt (Bluetooth)” http://msdn.microsoft.com/en-us/library/aa916547.aspx

* Win32
“Bluetooth and Socket Options” http://msdn.microsoft.com/en-us/library/aa362911(VS.85).aspx

We’ve now added support for the BlueZ D-Bus API.  So with it we now have support for:

  • Radio: Reading Mode and HardwareStatus is supported.
  • DiscoverDevices: Get Remembered devices, so we have full support there except for ‘live’ discovery.
  • BluetoothSecurity.RemoveDevice: Supported.
  • BluetoothSecurity.PairRequest: Support for pin=null case, that is where we want the system to prompt the local user for passphrase/etc.

We need to do more work to support Radio.set_Mode and fully automated PairRequest.

We have new support for the BlueZ Bluetooth protocol stack on Linux. We running on the Mono CLR there.

 Currently we have the following support:

  • Radio: We list the values propertly (Address, Name, CoD).  We do not get the Mode or HardwareStatus or allow setting them.   We will list all the installed radios, but see below for multi-radio usage.
  • Client.Connect: Supported.  No support for Authenticate/Encrypt/SetPin etc.
  • Data transfer: BlueZ uses Sockets for data transfer so everything should be fine. 🙂
  • Listener: Supported, including advertising SDP record.  Again no support for Auth/Encrypt/SetPin.
  • Client.DiscoverDevices: we discover only in-range devices.  There is no support currently for remembered devices and will return an error if they are requested — there doesn’t seem a ‘hci’ API.  There is also currently no support for ‘live’ discovery.
  • Security: No support currently.

As noted above BluetoothClient.Connect and BluetoothListener use Sockets.  Current versions of Mono do not support opening or using Bluetooth sockets.  My old pal Andy Hume and I have created a patch to Mono to enable this support, and have posted it as Mono/Novell bug id 655153.

Apply that patch to your Mono source code, rebuild, test and let me have your comments…

As noted above, unlike apparently every stack on Windows, BlueZ does support multiple radios. We do list all the radios and BluetoothClient and BluetoothListener will bind to a particular local address if requested to do so. However the other functionality does not currently ensure it runs on a particular radio, DiscoverDevices and GetServiceRecords (and as used internally by Connect) will use the primary radio.

We currently are using the original BlueZ ‘hci’ API. For many things their D-Bus based API is recommended instead, we will add support for that at some stage… There seems for instance no native API to get the list of remembered device, unless I’ve missed it…

In the Microsoft Bluetooth API on Win32 (desktop Windows) there’s no simple way to get only the in-range-discoverable devices (as I’ve noted previously one can specify whether one wants remembered and/or unknown devices, but the ‘unknown-only’ result has had any remembered devices removed from it!)  This has prevented us from providing the DiscoverDevice(discoverableOnly=true) feature that we provide on every other platform.  We also haven’t been able to provide the ‘live’ discovery feature for in-range devices as provided through class BluetoothComponent.

I’ve recently implemented support for using the native Bluetooth events on this platform (I first implemented this code a long time ago, and see below for why I didn’t use it earlier).  So now that we have support for the events we can implement both of the features discussed above.  Briefly the events are provided through the RegisterDeviceNotifications API and the events are raised as Window Messages.  Thus we create a window on which to receive the messages, creating a new message-loop in most cases for the window, and call the API to enable the events.

Thus when ‘live’ discovery is requested via BluetoothComponent, we run a normal discovery process so that the Bluetooth hardware searches for devices, and we monitor the native events at the same time and use the resulting events to know which devices are in-range (and obviously in discoverable mode) and raise the respective BluetoothComponent events.

We also use this functionality to provided the “discoverableOnly” feature.  By monitoring the native events we make a list of all the devices that are range, and at the same time we run a discovery asking for all remembered and unknown devices — which make Windows return the union of all remembered and in-range devices — then we filter that list leaving only the devices in it that we saw ‘in-range’ events for.  Phew a lot of work for a simple thing. 😦

We should note however that this is useful only on Windows 7 (and perhaps Vista — I haven’t tested); it not useful on Windows XP.  The native events aren’t raised there correctly so we can’t use them to provide this functionality. 😦

This code is in the repository now, please test it and let me hear your feedback.  (Download the code from http://32feet.codeplex.com/SourceControl/list/changesets, build the FX2 project (only), and reference and use it with your application.  Test BluetoothClient.DiscoverDevices(discoverableOnly=true) and BluetoothComponent.DiscoverDevicesAsync(discoverableOnly=true).  One can see the native events directly with BluetoothWin32Events — currently it exposed the Radio-In-Range and Radio-Out-Of-Range event only, we don’t expose the HCI or L2CAP events etc.

Differences between Windows XP and 7

The Radio-In-Range event (on Window 7 at least) raises lots of apparently duplicate events, so we need to do some filtering to provide both functions.  On Windows XP however the native event is not useful for device discovery; when a discovery process is run we do not see Radio-In-Range events for all discoverable devices, it seems like there are no events for devices we’ve seen before.

It seem that one only reliably gets Radio-In-Range events when a connection is made to a particular remote device (e.g. for BluetoothClient.Connect or BluetoothDeviceInfo.GetServiceRecords).  So it seems that the event isn’t raised for discovered devices, but only for devices connected to.  During discovery Windows connects to the newly seen devices to then to get their Device Name — which isn’t transferred in the discovery process in earlier Bluetooth versions.  That’s why we do see events for new devices during discovery.

So that’s why this code has been “on the shelve” for a while.  When tested on Windows XP no useful events were raised.  With Windows 7 reliable events are raised during discovery so we can use it now!

As of revision 78385 the following support is checked-in to our code repository at codeplex and is enabled by default. You can download the code from there and built the “InTheHand.Net.Personal” project to get the main library, the support will also be present in the next release.


We now have support for the BlueSoleil Bluetooth stack on desktop Windows. There is good support for BluetoothRadio, BluetoothDeviceInfo including RSSI, and device discovery including ‘live’ discovery. There is no support for BluetoothListener (there seems to be no BlueSoleil API), and there is only partial support for SDP querying (returns both partial records and partial attributes). BluetoothSecurity.RemoveDevice is supported, but more work is needed for PairRequest.

BluetoothClient.Connect is supported for RFCOMM/SPP connections. However connections can’t be made to profiles that BlueSoleil supports directly, for instance an RFCOMM connection to OBEX Push isn’t possible. Nor is it it possible to make a connection to a specific RFCOMM port number. BluetoothClient.Authenticate, Encrypt are not supported (no API support), and we haven’t looked at support for SetPin.

BluetoothClient’s data transfer is somewhat lacking in comparison with the other stacks as it doesn’t use Sockets nor has it an data transfer API like Widcomm. Instead it creates a virtual COM port for each connection, and that’s what we have to use. It creates a number of problems, firstly when there’s no data in the buffer for Read we would expect Read to block but instead we get an IO error, so we have to workaround that. Then on Write there seems a lack of flow control, we now internally split any any big writes into smaller chunks and in testing that seems to stop data being lost.

Due to the various restrictions above ObexWebRequest and ObexListener are not supported currently. It should be possible at least to support ObexWebRequest by internally using the BlueSoleil OBEX API directly, something for the future, and volunteers welcomed.

To check if BlueSoleil support is being loaded, check whether BluetoothRadio.PrimaryRadio or AllRadios returns a radio and whether its SoftwareManufacturer is a enum value of IvtBlueSoleil.

Please let me have any feedback.



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.

I have been working on providing support for the BlueSoleil stack on (desktop) Windows.  This stack seems a common one to be included with dongles, and people have been asking for it.  One good thing about it is that it supports RSSI measurements which the Microsoft stack on Windows does not. (I must write a post sometime of the Microsoft driver engineer’s reasons for not supporting RSSI).

I have checked the code into our code repository at 32feet.codeplex.com and would be pleased to get your feedback.

Download the code from there (whether as a ZIP or as a SVN checkout etc) and compile the main library (InTheHand.Net.Personal.FX2) (actually v3 would likely do) and the SeparateBlueSoleil project.  Then follow the instructions below — which are from the “32feet BlueSoleil Test.html” file which is included in the repository.

Radio, DeviceInfo, and Discovery including ‘live discovery’ are working well.  BluetoothClient is ok, but there is no support currently(?) for Listener.  There’s still work to be done on checking what fails when the radio is disabled, and also on mapping of BlueSoleil’s errors into SocketException etc errors — currently a special BluesoleilException is thrown for any BlueSoleil API error.

Data transfer is less good however.  Rather than providing access to the connection to the remote RFCOMM endpoint as a socket or through another API, the access instead is through a virtual COM port, which the stack creates automatically when an RFCOMM connection is made.  This causes a number of problem, (no notification of disconnect, error on reading when the buffer is empty, data loss when writing too much), which I should write about elsewhere.  I’ve done a lot of work to workaround those problems so things should be ok.  Let me know what you find.

Alan


Usage

  1. Compile the SeparateBlueSoleil project — the code will be included in the main assembly eventually.
  2. Tell the library to load the code from there. To do this use a “app.confile” file containing the following, renamed to match your application e.g. to “myapp.exe.config”:
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <sectionGroup name="InTheHand.Net.Personal">
          <section name="BluetoothFactory"
              type="InTheHand.Net.Bluetooth.BluetoothFactorySection, InTheHand.Net.Personal"
              requirePermission="false" />
        </sectionGroup>
      </configSections>
      <!-- -->
      <InTheHand.Net.Personal>
        <BluetoothFactory reportAllErrors="false" oneStackOnly="false">
          <stackList>
            <add name="InTheHand.Net.Bluetooth.BlueSoleil.BluesoleilFactory, SeparateBlueSoleil" />
          </stackList>
        </BluetoothFactory>
      </InTheHand.Net.Personal>
      <!-- -->
      <system.diagnostics>
        <trace autoflush="true" indentsize="4">
          <listeners>
            <add name="myListener"
               type="System.Diagnostics.TextWriterTraceListener"
               initializeData="trace.log" />
          </listeners>
        </trace>
      </system.diagnostics>
    </configuration>
  3. Check that the support has loaded.
    1. Check whether BluetoothRadio.PrimaryRadio and/or BluetoothRadio.AllRadios are non-empty, and that one has SoftwareManufacter containing a BlueSoleil value.
    2. Check what’s in the trace.log file. It should contain something like the following.
      BlueSoleil Btsdk_Init: OK=0x0000
      BlueSoleil IsBluetoothReady: True, IsBluetoothHardwareExisted: True
      BlueSoleil Btsdk_StartBluetooth: OK=0x0000
      BlueSoleil IsBluetoothReady: True, IsBluetoothHardwareExisted: True
      Num factories: 1, Primary Factory: BluesoleilFactory
      BlueSoleil Radio mode: BTSDK_GENERAL_DISCOVERABLE, BTSDK_CONNECTABLE, BTSDK_PAIRABLE
  4. See below for what is and isn’t supported.

—- 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)
Try
Thread.VolatileWrite(m_disposed, 1)
m_disposedEvent.Set()
' Should we close the Stream? It makes life much easier for us if
' we do but
Finally
MyBase.Dispose(disposing)
End Try
End Sub

Private ReadOnly Property IsDisposed() As Boolean
Get
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
Try
#If False Then
readLen = m_strm.Read(buf, 0, buf.Length)
#Else
' 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)
Else
' 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
DoError(ex)
Exit While
End Try
If readLen = 0 Then
DoConnectionClosed()
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()
Get
Return m_data
End Get
End Property
End Class

In 2.5 on Win32 we made all calls into the Widcomm API come from one thread, this was due to the note in the Widcomm documentation as discussed at https://32feetnetdev.wordpress.com/2009/12/04/widcomm-single-thread-access/   However since then two people noted in the forums that on Windows Mobile some operations failed when called from an async callback.  One noted that in a DiscoverDevices callback that calling BeginDiscoverDevices failed, and another noted that a Connect failed when called from there.

So what do we learn from this?  If a library doesn’t state its threading requirements then assume the worst.  This is unlike the managed code I’ve created where everything is safe when called from multiple threads and from a callback.  I ensure that all invariants are correct and am holding no locks etc when I raise the callback etc.  So nothing is forbidden, and no special main thread is required.

So, since then in the Widcomm support code on both platforms we’ve made all user callbacks occur on a thread-pool thread.  Hopefully that’ll solve all the threading issues once and for all.  On the downside it could make a small change in behaviour, for instance the callbacks could take a slightly longer period to start after an event, and if there were multiple reads or writes released by a stack event then they could now run in parallel.

Since I produced the 2.4 release there has been an update to the VC++ runtime files, presumably as part of one of the ATL security patches.  This means that the new 32feetWidcomm DLL on Win32 references that updated version.  The manifest in the 2.4 version contains:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT"
version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT"
version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

but the new version contains:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT"
version="8.0.50727.4053" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT"
version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

So we need VC runtime version 8.0.50727.4053 rather than 8.0.50727.762.  If you run this on a machine that doesn’t have that version it will fail with e.g.

Exception creating factory ‘InTheHand.Net.Bluetooth.Widcomm.WidcommBluetoothFactory, ex: System.DllNotFoundException: Unable to load DLL ’32feetWidcomm’: The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail. (Exception from HRESULT: 0x800736B1)

And there should be a couple of events in the event log, like the following (but they’re for a DEBUG version fault) e.g.

Log Name:      Application
Source:        SideBySide
Date:          14/12/2009 09:42:30
Event ID:      33
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      alanlt2w
Description:
Activation context generation failed for “E:\Users\alan\Documents\32feet Merge\32feetWidcomm.DLL”. Dependent Assembly Microsoft.VC80.DebugCRT,processorArchitecture=”x86″,publicKeyToken=”1fc8b3b9a1e18e3b”,type=”win32″,version=”8.0.50727.4053″ could not be found. Please use sxstrace.exe for detailed diagnosis.

Log Name:      Application
Source:        SideBySide
Date:          14/12/2009 09:42:30
Event ID:      33
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      alanlt2w
Description:
Activation context generation failed for “E:\Users\alan\Documents\32feet Merge\32feetWidcomm.DLL”. Dependent Assembly Microsoft.VC80.DebugCRT,processorArchitecture=”x86″,publicKeyToken=”1fc8b3b9a1e18e3b”,type=”win32″,version=”8.0.50727.4053″ could not be found. Please use sxstrace.exe for detailed diagnosis.

So how to fix this.  Install vcredist.exe from “Microsoft Visual C++ 2005 Service Pack 1 Redistributable Package ATL Security Update” http://www.microsoft.com/downloads/details.aspx?familyid=766a6af7-ec73-40ff-b072-9112bab119c2&displaylang=en

After I install that I get five new items in the SxS cache (%windir%\winsxs\):

x86_microsoft.vc80.atl_1fc8b3b9a1e18e3b_8.0.50727.4053_none_d1c738ec43578ea1
x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.4053_none_d08d7da0442a985d
x86_microsoft.vc80.mfcloc_1fc8b3b9a1e18e3b_8.0.50727.4053_none_03ca5532205cb096
x86_microsoft.vc80.mfc_1fc8b3b9a1e18e3b_8.0.50727.4053_none_cbf21254470d8752
x86_microsoft.vc80.openmp_1fc8b3b9a1e18e3b_8.0.50727.4053_none_3b0e32bdc9afe437

The second one being the required component.

Design a site like this with WordPress.com
Get started