How can I reference the Windows 8 Runtime (specifi

2019-03-17 00:45发布

问题:

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.

回答1:

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.



回答2:

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