Search code examples
c++windowsaudioc++-winrtwrl

How to initialize WinRT AudioGraphSettings using WRL ComPtr?


With C++/WinRT the AudioGraphSettings can be easily initialized with its constructor:

AudioGraphSettings settings(AudioRenderCategory::Media);

I'm having trouble to use it inside my WRL project. Below is my implementation

ComPtr<IAudioGraphSettings> settings;
Windows::Foundation::GetActivationFactory(
    HStringReference(RuntimeClass_Windows_Media_Audio_AudioGraphSettings).Get(),
    &settings
);

The settings still null and I don't know how to initialize it with the required AudioRenderCategory constructor.

If I do it like below, I got access violation crash because it's still null.

settings->put_AudioRenderCategory(AudioRenderCategory::AudioRenderCategory_Media);

Solution

  • Type activation at the ABI level is more involved than, for example, instantiating types in C++. The admittedly terse documentation outlines the different mechanisms:

    WinRT defines three activation mechanisms: direct activation (with no constructor parameters), factory activation (with one or more constructor parameters), and composition activation.

    The IAudioGraphSettings type falls into the second category: It instantiates a type based on a single argument of type AudioRenderCategory. Instantiating a type is thus a two-phase process:

    1. Retrieve an activation factory
    2. Use the activation factory to instantiate a type

    The code in the question conflates both operations, and the call to GetActivationFactory returns an HRESULT value of 0x80004002, i.e. E_NOINTERFACE. It needs to request the IAudioGraphSettingsFactory interface instead, and subsequently use that factory to instantiate the IAudioGraphSettings type.

    The following is a complete implementation that illustrates how to activate an IAudioGraphSettings type:

    #include <wrl/wrappers/corewrappers.h>
    #include <wrl/client.h>
    
    #include <windows.foundation.h>
    #include <windows.media.audio.h>
    #include <windows.media.render.h>
    
    #pragma comment(lib, "windowsapp")
    
    using namespace Microsoft::WRL;
    using namespace Microsoft::WRL::Wrappers;
    using namespace ABI::Windows::Foundation;
    using namespace ABI::Windows::Media::Audio;
    using namespace ABI::Windows::Media::Render;
    
    int main()
    {
        RoInitializeWrapper init { RO_INIT_MULTITHREADED };
        HRESULT hr { init };
        if (FAILED(hr))
            return hr;
    
        // Retrieve activation factory
         ComPtr<IAudioGraphSettingsFactory> settings_factory {};
         hr = GetActivationFactory(
             HStringReference(RuntimeClass_Windows_Media_Audio_AudioGraphSettings).Get(),
             &settings_factory);
         if (FAILED(hr))
             return hr;
    
         // Use activation factory to instantiate type
         ComPtr<IAudioGraphSettings> settings {};
         hr = settings_factory->Create(AudioRenderCategory_Media, &settings);
         if (FAILED(hr))
             return hr;
    
        return hr;
    }
    

    This is how things look at the ABI level, which is ultimately where the WRL lives. C++/WinRT takes care of all the boilerplate code, and neatly maps parameterized factory activation onto C++ constructor implementations. That's why the C++/WinRT implementation is so much more compact.