locked
Listing Bluetooth devices: Different results in C# and in JavaScript

    Question

  • Hello,

    I am trying to use WinJS to list Bluetooth devices accessible from my machine. When I use the code from the samples, I get about 30 devices including many devices that are not Bluetooth (keyboard, hard drive, ...)

    This is the code I use in WinJS:

    Windows.Devices.Enumeration.DeviceInformation.findAllAsync(
            Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService.getDeviceSelector(
                Windows.Devices.Bluetooth.Rfcomm.RfcommServiceId.obexObjectPush
                //Windows.Devices.Bluetooth.Rfcomm.RfcommServiceId.serialPort
            )
        )
        .done(
            this.onFindAllDevices.bind(this, callback),
            this.onError.bind(this)
        );
    

    SerialPort and obexObjectPush give the same result.

    When I do it in C#, I only get 2 devices, which is the right number that it should detect. This is the exact same APIs being used:

    var serviceInfoCollection = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService.GetDeviceSelector(Windows.Devices.Bluetooth.Rfcomm.RfcommServiceId.SerialPort));

    I believe this is a bug in the WinJS APIs, how can it be fixed or reported?

    Thanks,

    Fabien

    Tuesday, May 20, 2014 8:06 AM

Answers

  • This is not a bug: it's a quirk in the design of the findAllAsync API in WinRT and how its overloads are projected into JavaScript. I explain this in my blog, http://kraigbrockschmidt.com/blog/?p=1268, but let me repeat here.

    If you look carefully at the documentation for findAllAsync (http://msdn.microsoft.com/en-us/library/windows/apps/xaml/br225436.aspx), you'll see that there are four overloads:
    • FindAllAsync() | findAllAsync()
    • FindAllAsync(DeviceClass) | findAllAsync(DeviceClass)
    • FindAllAsync(String)
    • FindAllAsync(String, IIterable(String)) | findAllAsync(string, IIterable(String))

    Notice that the third one is NOT listed with a JavaScript variant (e.g. findAllAsync(String)), because this variant is not projected into JavaScript. JavaScript has the limitation that it cannot differentiate overloads with the same number or arguments (arity), thus the WinRT designers had to choose which of the middle two would be projected, and chose the one that takes a DeviceClass.

    The unfortunate side-effect of this, however, is that a DeviceClass is an enumeration, whose values in JavaScript also project as strings. Therefore, you won't get an error if you pass any random string to findAllAsync(DeviceClass), because it just ignores values that aren't in the enum and uses the default of DeviceClass.all. As a result, when you pass a selector string, you're basically calling findAllAsync(DeviceClass.all) and therefore every device on the system gets enumerated. This doesn't happen with the C# version because its strong typing makes sure to call findAllAsync(String) as you expect.

    The solution, fortunately, is simple. Call findAllAsync(<selector string>, null) to specifically call the fourth variant that does take a selector string. Then you'll see the expected results.

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press.

    Tuesday, May 20, 2014 3:08 PM

All replies

  • I'll get our BT guys to take a look at this.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Tuesday, May 20, 2014 12:28 PM
    Moderator
  • Thanks a lot !
    Tuesday, May 20, 2014 2:08 PM
  • This is not a bug: it's a quirk in the design of the findAllAsync API in WinRT and how its overloads are projected into JavaScript. I explain this in my blog, http://kraigbrockschmidt.com/blog/?p=1268, but let me repeat here.

    If you look carefully at the documentation for findAllAsync (http://msdn.microsoft.com/en-us/library/windows/apps/xaml/br225436.aspx), you'll see that there are four overloads:
    • FindAllAsync() | findAllAsync()
    • FindAllAsync(DeviceClass) | findAllAsync(DeviceClass)
    • FindAllAsync(String)
    • FindAllAsync(String, IIterable(String)) | findAllAsync(string, IIterable(String))

    Notice that the third one is NOT listed with a JavaScript variant (e.g. findAllAsync(String)), because this variant is not projected into JavaScript. JavaScript has the limitation that it cannot differentiate overloads with the same number or arguments (arity), thus the WinRT designers had to choose which of the middle two would be projected, and chose the one that takes a DeviceClass.

    The unfortunate side-effect of this, however, is that a DeviceClass is an enumeration, whose values in JavaScript also project as strings. Therefore, you won't get an error if you pass any random string to findAllAsync(DeviceClass), because it just ignores values that aren't in the enum and uses the default of DeviceClass.all. As a result, when you pass a selector string, you're basically calling findAllAsync(DeviceClass.all) and therefore every device on the system gets enumerated. This doesn't happen with the C# version because its strong typing makes sure to call findAllAsync(String) as you expect.

    The solution, fortunately, is simple. Call findAllAsync(<selector string>, null) to specifically call the fourth variant that does take a selector string. Then you'll see the expected results.

    Kraig

    Author, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, a free ebook from Microsoft Press.

    Tuesday, May 20, 2014 3:08 PM
  • Hello Kraig,

    Thanks so much for the detailed answer, I get what is happening now. 

    Just tried it, works perfectly, thanks a lot!

    Fabien

    Tuesday, May 20, 2014 3:39 PM