3

I am using C# in Visual Studio Professional 13 on Windows 8.1 with the WDK installed.

I need to write a desktop application that interacts with a BLE device using a custom Service UUID. Using the Bluetooth Generic Attribute Profile - Heart Rate Service sample project available from MSDN, I am able to edit the service UUID being searched for and find my specific device.

However, the sample project is a Windows Store (Metro) App, and I need a console app.

When I create a new project of type Visual C# > Store App > Windows App, the Windows 8 SDK is automatically included in the project.

BUT when creating a Visual C# > Windows Desktop > * project, I cannot find a way to include the Windows 8 Runtime AND the BLE API that I need to access.

Certainly Microsoft wasn't so short-sighted as to restrict the BLE API to Store Apps? How does one create/modify their project to develop desktop and console applications that utilize the BLE API?

The research (and failed attempts) I have done thus far has already ruled out 32feet.net as the library currently does not provide support for the bluetooth low-energy stack.

However, if there is another 3rd party library (preferably open source, or at least one with a trial version) that provides BLE support, I would be open to using that in lieu of the Windows 8 Runtime.

Scott Offen
  • 6,933
  • 3
  • 21
  • 24
  • You cannot have it both ways. That api was specifically designed to be used in Store apps, console mode apps are not possible. Talking to Bluetooth devices of course was possible before the Store came around, you'll need to go shopping for a different api. The MSDN landing page [is here](http://msdn.microsoft.com/en-us/library/windows/desktop/aa362932%28v=vs.85%29.aspx). – Hans Passant Jul 19 '14 at 17:39

2 Answers2

6

It is possible to call WinRT APIs from Win32 desktop apps on Windows 8.x, but this is not a well-tested or more importantly well-documented scenario.

With C#, you have to manually add the project and runtimes references to get this to work. This blog post covers it in detail. In short, to get the "Core" tab to appear in your project settings you need to manually add this to your Visual Studio project per MSDN.

<PropertyGroup>
    <TargetPlatformVersion>8.0</TargetPlatformVersion>
</PropertyGroup>

Then manually add a reference to both System.Runtime.dll and System.Runtime.InteropServices.WindowsRuntime.dll.

BTW, for C++, you can make use of ABI namespaces to call WinRT functions (such as I'm doing in one case in DirectXTK for Audio) or you can use C++/CX extensions.

#if defined(__cplusplus_winrt)

    // Enumerating with WinRT using C++/CX (Windows Store apps)
    using Windows::Devices::Enumeration::DeviceClass;
    using Windows::Devices::Enumeration::DeviceInformation;
    using Windows::Devices::Enumeration::DeviceInformationCollection;

    auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
    while (operation->Status != Windows::Foundation::AsyncStatus::Completed)
        ;

    DeviceInformationCollection^ devices = operation->GetResults();

    for (unsigned i = 0; i < devices->Size; ++i)
    {
        using Windows::Devices::Enumeration::DeviceInformation;

        DeviceInformation^ d = devices->GetAt(i);
...
    }
#else

    // Enumerating with WinRT using WRL (Win32 desktop app for Windows 8.x)
    using namespace Microsoft::WRL;
    using namespace Microsoft::WRL::Wrappers;
    using namespace ABI::Windows::Foundation;
    using namespace ABI::Windows::Foundation::Collections;
    using namespace ABI::Windows::Devices::Enumeration;

    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    HRESULT hr = initialize;
    ThrowIfFailed( hr );

    Microsoft::WRL::ComPtr<IDeviceInformationStatics> diFactory;
    hr = ABI::Windows::Foundation::GetActivationFactory( HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &diFactory );
    ThrowIfFailed( hr );

    Event findCompleted( CreateEventEx( nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS ) );
    if ( !findCompleted.IsValid() )
        throw std::exception( "CreateEventEx" );

    auto callback = Callback<IAsyncOperationCompletedHandler<DeviceInformationCollection*>>(
        [&findCompleted,list]( IAsyncOperation<DeviceInformationCollection*>* aDevices, AsyncStatus status ) -> HRESULT
    {
        UNREFERENCED_PARAMETER(aDevices);
        UNREFERENCED_PARAMETER(status);
        SetEvent( findCompleted.Get() );
        return S_OK;
    });

    ComPtr<IAsyncOperation<DeviceInformationCollection*>> operation;
    hr = diFactory->FindAllAsyncDeviceClass( DeviceClass_AudioRender, operation.GetAddressOf() );
    ThrowIfFailed( hr );

    operation->put_Completed( callback.Get() );

    (void)WaitForSingleObjectEx( findCompleted.Get(), INFINITE, FALSE );

    ComPtr<IVectorView<DeviceInformation*>> devices;
    operation->GetResults( devices.GetAddressOf() );

    unsigned int count = 0;
    hr = devices->get_Size( &count );
    ThrowIfFailed( hr );

    if ( !count )
        return list;

    for( unsigned int j = 0; j < count; ++j )
    {
        ComPtr<IDeviceInformation> deviceInfo;
        hr = devices->GetAt( j, deviceInfo.GetAddressOf() );
        if ( SUCCEEDED(hr) )
        {
            HString id;
            deviceInfo->get_Id( id.GetAddressOf() );

            HString name;
            deviceInfo->get_Name( name.GetAddressOf() );
...
        }
    }

#endif 

This of course is code that is only compatible with Windows 8.0 or later and will not run on Windows 7 or earlier.

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81
  • I'm upvoting this because it is a really good answer, and you're spot on with the method to call the Windows 8 Runtime from inside a a desktop application and use many of the API therein. However, I can't accept this as the correct answer as this method still does not provide access to the BLE API. As @hans-passant pointed out, the BLE API is (currently) only accessible in windows store apps, AFAIK. If you know of specific details to overcome *that* limitation, please add them and I'll accept this answer. Per MSDN: http://goo.gl/mj7yc4 – Scott Offen Jul 21 '14 at 20:03
  • 1
    The ABI method should work for all WinRT APIs available on Windows 8.x, but as I said you unfortunately have to work it out without any documentation. The actual issue here is that the BLE API is not supported on Windows 8.x at all even for Windows Store apps. It's only currently on Windows phone 8.1 and the MSDN page doesn't really express that (Minimum supported client/server of "None supported", the text "[Windows Store apps only]" is not relevant). – Chuck Walbourn Jul 21 '14 at 23:02
  • It also appears that the required header for the Windows version of this API is missing from the Windows 8.x SDK. It's only in the .winmd and not in "Windows.Devices.Bluetooth.h". So the answer is currently you can't consume it from a Win32 desktop app at all. – Chuck Walbourn Jul 21 '14 at 23:09
-1

Create a Portable Class Library and put all your BLE code there then reference the library in your desktop/console application.

Nikola Despotoski
  • 49,966
  • 15
  • 119
  • 148