1

I have a bespoke bluetooth device that I can pair with and connect to using windows 10 and it creates 2 com ports - one is listed as incoming and one is listed at outgoing.

When I connect using the 32Feet C# bluetooth libraries I am able to discover and pair the device and the enable the SPP profile but, alas, I only get one COM port and it is listed as "outgoing".

I am required to interface to the device using someone else's code and it needs to be supplied a com port number. Unfortunately it wants to connect to the "incoming" port.

Hence my question is what magic do I need to create this incoming com port? I have looked at the 32Feet code and the underlying API call of BluetoothSetServiceState(...) and it doesn't seem to have any parameters to control how the ports are created. Is there another profile for this feature??

Paul Coldrey
  • 1,389
  • 11
  • 20
  • COM ports (and bluetooth SPP) are bi-directional. You don't need any magic here – vasily.sib Jun 04 '18 at 08:10
  • Unfortunately magic is required - when Windows pairs the device I get two ports created and the "incoming" one provides the connectivity I need. When I use SPP via 32Feet I only get 1 port and it doesn't work. I know this seems silly,... but alas I don't control the whole stack so I need the magic. From my reading it seems both the "incoming" and "outgoing" ports are bi-directional but one is created by the "client" and one is created by the "server" – Paul Coldrey Jun 04 '18 at 08:15

3 Answers3

2

You have to use undocumented InstallIncomingComPort function from BluetoothAPIs.dll

  • Hi Mike, So when you said "undocumented" you really meant it! Do you happen to know what parameters it expects - I am guessing it needs a handle to the local radio (which I can probably send a null pointer) and then the BLUETOOTH_DEVICE_INFO for the remote device,... but then is there anything else? Perhaps on/off state? – Paul Coldrey Jun 04 '18 at 23:42
  • 1
    The first parameter is radio handle. The second one is a pointer to the value returned by the FindNextOpenVCOMPort function. – Mike Petrichenko Jun 05 '18 at 07:37
  • Looking on Google it seems FindNextOpenVCOMPort might also be an undocumented function! Does it just take the radio handle or is it parameterless? – Paul Coldrey Jun 05 '18 at 10:39
  • 1
    Yes, this is also undocumented function. It takes radio handle and returns memory block that represents a vCOM and you have to pass this memory block to InstallIncomingComPort. – Mike Petrichenko Jun 05 '18 at 13:24
  • 1
    Just remembered other solution: BluetoothSetLocalServiceInfo – Mike Petrichenko Jun 05 '18 at 13:30
  • So Mike, is this stuff used in your BT Framework code? It seems the whole area is really poorly documented by M$ and I can only wonder how you unraveled the puzzle. If you have some code that does this stuff then I am happy to buy it because otherwise we will have 1000 comments here before I delve far enough into your knowledge to actually code a solution. – Paul Coldrey Jun 05 '18 at 23:23
  • Find the cod ein next answer cause I am not able to install it in comment. – Mike Petrichenko Jun 06 '18 at 07:20
2
private const UInt16 BLUETOOTH_MAX_SERVICE_NAME_SIZE = 256;
private const UInt16 BLUETOOTH_DEVICE_NAME_SIZE  = 256;

private static Guid SerialPortServiceClass_UUID = new Guid("{00001101-0000-1000-8000-00805F9B34FB}");

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct BLUETOOTH_LOCAL_SERVICE_INFO
{
            public Boolean Enabled;
            public Int64 btAddr;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BLUETOOTH_MAX_SERVICE_NAME_SIZE)]
            public String szName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BLUETOOTH_DEVICE_NAME_SIZE)]
            public String szDeviceString;
};

[DllImport("BluetoothAPIs.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern UInt32 BluetoothSetLocalServiceInfo(IntPtr hRadioIn, ref Guid pClassGuid, UInt32 ulInstance, ref BLUETOOTH_LOCAL_SERVICE_INFO pServiceInfoIn);

private void CreateComPort(Boolean Create)
{
            BLUETOOTH_LOCAL_SERVICE_INFO s = new BLUETOOTH_LOCAL_SERVICE_INFO();
            s.btAddr = 0;
            s.Enabled = Create;
            s.szName = "MyComPort";
            s.szDeviceString = "COM10";

            UInt32 Res = BluetoothSetLocalServiceInfo(IntPtr.Zero,
                ref SerialPortServiceClass_UUID, 1, ref s);
            MessageBox.Show(Res.ToString());
}
  • For all those who follow after me note that you probably want to set s.btAddr to the address of your bluetooth device. See answer below for my code that was put into the 32Feet code. – Paul Coldrey Jun 12 '18 at 04:29
  • You do not need the MAC address for incomming service. Providing the MAC should allow to connect to only specific device but if you look into its implementation (you can try with IDA) - it actually does nothing. BTW for 32feet: I would like to ask to remove all my code from your library that you did not receive directly from me. For example the code that changes local radio name on MS stack. That code was provided to one of my customer who shared it with 32feet with EULA violation. – Mike Petrichenko Jun 22 '18 at 01:00
0

If you are looking to use the InTheHand BT library and get an incoming com port you can add the following code to the bottom of the function

public void SetServiceState(Guid service, bool state, bool throwOnError)

in WindowsBlurtoothDeviceInfo.cs

if (service == BluetoothService.SerialPort)
{
    NativeMethods.BLUETOOTH_LOCAL_SERVICE_INFO s = new NativeMethods.BLUETOOTH_LOCAL_SERVICE_INFO();
    s.btAddr = deviceInfo.Address;
    s.Enabled = state;
    s.szName = "RemScan";
    s.szDeviceString = "COM10";
    UInt32 Res = NativeMethods.BluetoothSetLocalServiceInfo(IntPtr.Zero, ref NativeMethods.SerialPortServiceClass_UUID, 1, ref s);
}
Paul Coldrey
  • 1,389
  • 11
  • 20