Multi-Stack


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.

So, I found a little time to continue investigations after my debugging session, Investigating Widcomm when MSFT present   Of the possibilities I’ve emailed Broadcom for #1 and also tried #3.  (We’ll leave #2 for now).

So I downloaded version 5.1.0.3101 of the SDK — that’s the last version of the SDK before v6 added the Vista support (wrapping the MSFT stack).  And guess what, it works!  As simple as that…

Running the multi-stack tests in ConsoleMenuTesting is successful.  That’s running a listener on one stack and the client on the other stack, both on the same PC.  Both cases work, listener on Widcomm and listener on MSFT, see the logs below.  Also running two ObexWebrequests to the opposite stack is also successful.

All that’s needed is a new copy of our native DLL, 32feetWidcomm.dll.  I’ve posted a copy to http://32feet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=37167.  Please download this, replace your current copy and test test test.  Let me know if it’s all successful, or not…

option>12
 1 -- Quit
 2 -- <-Back
 3 -- ListenerOnStack1ClientOnStack2
 4 -- ListenerOnStack2ClientOnStack1
...
option>3
IBtIf using WidcommStBtIf.
BtIf_Create
Num factories: 2, Primary Factory: WidcommBluetoothFactory
BtIf_GetLocalDeviceVersionInfo
BtIf_GetLocalDeviceName
BtIf_IsDeviceConnectableDiscoverable
1)  Radio, address: 00:0A:3A:68:65:BB
Mode: Discoverable
Name: ALANPC1b, LmpSubversion: 777
ClassOfDevice: 0, device: Miscellaneous / service: None
Software: Broadcom,  Hardware: Broadcom, status: Running
Remote: ''2)  Radio, address: 00:80:98:24:4C:A4
Mode: Connectable
Name: ALANPC1, LmpSubversion: 524
ClassOfDevice: 20104, device: DesktopComputer / service: Network
Software: Microsoft,  Hardware: CambridgeSiliconRadio, status: Unknown
Remote: ''IRfCommIf using WidcommStRfCommIf.
RfCommIf_Create
RfCommIf_GetScn
Server GetScn returned port: 10
True; False,False-> NONE
Listening on 000000000000:10
WidcommRfcommPort.Create'd: 1212FC8
NativeMethods.RfcommPort_OpenServer ret: SUCCESS=0x00000000
OpenServer ret: SUCCESS=0x00000000
StartOneNewListenerPort 1212FC8.
Started 1 new port(s).
SdpService_Create
SdpService_AddServiceClassIdList, num: 1, p_service_guids: 0135CC90
SdpService_AddRFCommProtocolDescriptor
Listener active on SCN: 10
BeginAccept Enqueued
Gonna connect to: 000A3A6865BB:10
release to connect!>
HandleEvent: 512=0x200=CONNECTED
1212FC8: CONNECTED (New)CONNECTED 1212FC8; m_state: New; m_arConnect (set), IsCompleted: False.
NOT RfcommPort_IsConnected on our Widcomm SINGLE thread!
RfcommPort_IsConnected on Widcomm callback thread!
Client Connected to : 'ALANPC1b' 000A3A6865BB:10
waiting for Lsnr.Accept completion event.
PortAccepted Dequeued a caller
WidcommRfcommPort.Create'd: 1215EF8
NativeMethods.RfcommPort_OpenServer ret: SUCCESS=0x00000000
OpenServer ret: SUCCESS=0x00000000
StartOneNewListenerPort 1215EF8.
Started 1 new port(s).
HandleEvent: 7224=0x1C38=CTS, DSR, RLSD, CTSS, DSRS, RLSDS
1212FC8: CTS, DSR, RLSD, CTSS, DSRS, RLSDS (Connected)All success

….

1 -- Quit
2 -- <-Back
3 -- ListenerOnStack1ClientOnStack2
4 -- ListenerOnStack2ClientOnStack1
...
Invalid number>4
BtIf_GetLocalDeviceVersionInfo
BtIf_GetLocalDeviceName
BtIf_IsDeviceConnectableDiscoverable
1) Radio, address: 00:0A:3A:68:65:BB
Mode: Discoverable
Name: ALANPC1b, LmpSubversion: 777
ClassOfDevice: 0, device: Miscellaneous / service: None
Software: Broadcom, Hardware: Broadcom, status: Running
Remote: ''2) Radio, address: 00:80:98:24:4C:A4
Mode: Connectable
Name: ALANPC1, LmpSubversion: 524
ClassOfDevice: 20104, device: DesktopComputer / service: Network
Software: Microsoft, Hardware: CambridgeSiliconRadio, status: Unknown
Remote: ''Listener active on SCN: 1
IRfCommIf using WidcommStRfCommIf.
WidcommRfcommPort.Create'd: 1215A78
RfCommIf_Create
Gonna connect to: 008098244CA4:1
release to connect!>
BeginFillInPortState
BeginFillInPort, has port -> Completed Syncronously
NativeMethods.RfcommPort_OpenClient ret: SUCCESS=0x00000000
OpenClient ret: SUCCESS=0x00000000
HandleEvent: 512=0x200=CONNECTED
1215A78: CONNECTED (New)CONNECTED 1215A78; m_state: New; m_arConnect (set), IsCompleted: False.
NOT RfcommPort_IsConnected on our Widcomm SINGLE thread!
RfcommPort_IsConnected on Widcomm callback thread!
HandleEvent: 7736=0x1E38=CTS, DSR, RLSD, CONNECTED, CTSS, DSRS, RLSDS
Client Connected to : 'ALANPC1' 008098244CA4:00000000000000000000000000000000
waiting for Lsnr.Accept completion event.
All success1215A78: CTS, DSR, RLSD, CONNECTED, CTSS, DSRS, RLSDS (Connected)
CONNECTED 1215A78; m_state: Connected; m_arConnect (null), IsCompleted: n/a.
HandleEvent: 512=0x200=CONNECTED
1215A78: CONNECTED (Connected)
CONNECTED 1215A78; m_state: Connected; m_arConnect (null), IsCompleted: n/a.

I’ve previously run the Widcomm stack under a debugger but never with enough time to investigate it thoroughly.  Having changed the threading model and since that didn’t do anything to help Widcomm work when the Microsoft stack is present I wanted to get stuck in to fixing this.

In short what I found was that when the Widcomm stack loads (actually when I create a BluetoothListener on the Widcomm stack) I see calls to the Microsoft stack API and to Winsock!  First it calls the BluetoothFindFirstRadio/etc API and then it calls: socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM).  Then when the BluetoothListener is active if one breaks into the debugger and lists the threads one thread is sitting waiting on WSAAccept.

So it’s all very clear now.  It’s not some unintentional conflict between Widcomm and the Microsoft Bluetooth stack that breaks some threading or message passing system, instead its fully intentional behaviour on Widcomm’s part.  For Vista instead of porting their whole stack to the new platform they instead just provide a wrapper around the Microsoft stack.  And this is what’s causing the problems here.  We want the Widcomm API to use the Widcomm stack but it’s finding the Microsoft stack, and assuming that’s its own stack isn’t present.  So we need a way to tell Widcomm, “Don’t use the Microsoft stack”.

In order to test whether that’s all that is required, I used the debugger to make it appear like the Microsoft stack wasn’t present.  I set a breakpoint on ws2_32.dll!socket and the breakpoint was hit with Widcomm doing the call above.  I arranged for the socket creation to fail and thus forced Widcomm to use its own stack.  Doing this made the two stacks coexist. 🙂  I was able to get the two stacks to connect to each other on the same PC: a BluetoothClient on Widcomm connecting to a BluetoothListener on Widcomm, and the also with the roles reversed.

So how can we get Widcomm to ignore the Microsoft stack:

  1. Find if there’s a a Registry key or similar that Widcomm provides to disable the wrapping behaviour. 
  2. Do some cunning interception of the socket API call and cause it to fail when Widcomm attempts to open a Bluetooth socket.
  3. Try an older version of the Widcomm SDK to see if it provides the wrapping behaviour and thus the feature isn’t present in an older version.  There’s the danger that we lose some features present in up-to-date SDKs.

Don’t know when I’ll next have some time to do some more investigation so feel free to carry out your own investigations in the meantime.

Since starting on the Widcomm support its always been hoped that we could allow applications to use both the Microsoft and Widcomm stack at the same time (or any combinations of stacks supported on the machine).  Then an application could connect to more devices than it could when just using one stack alone.  This is partly necessary because those two stacks each only support one connected radio (Bluetooth Controller) — unlike BlueZ on Linux for instance which supports more than one.

I’ve added support for loading two or more stack types at a time in the library in preparation for this (it’s disabled in the 2.4 release however).  However, although I’ve not had a lot of time to play with this, I haven’t managed to get the two stacks to work at the same time.  It appears that even if the MSFT stack support isn’t loaded but I have my MSFT-stack dongle connected then the Widcomm API doesn’t work…  Has anyone else seen this?  Maybe it’s Widcomm version dependent?  I’m using 3.0.1.912 (via Belkin).

Has anyone else tried this scenario?

 

There are various ways to disable loading the MSFT stack.  Either run in the debugger and set a breakpoint in InTheHand.Net.Bluetooth.BluetoothFactory.GetStacks_inLock, and don’t allow the loop to load the MSFT stack factory.  Or, the correct way is to use configuration settings.  After 2.4 I’ve added this support.  Add a configuration file like the following to disable the MSFT stack.

<?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>
      <stackList>
        <remove name="InTheHand.Net.Bluetooth.SocketsBluetoothFactory" />
      </stackList>
    </BluetoothFactory>
  </InTheHand.Net.Personal>
</configuration>

And in passing, to enable multi-stack support in the library (with all errors reported) use a configuration file with the following element. 🙂

...
<BluetoothFactory reportErrors="true"
    oneStackOnly="false" >
</BluetoothFactory>
...