Christophe Pichaud on Microsoft Technologies - MVP Developer Technologies 2019-2020
Using WRL to implement Async methods with Standard ISO C++WRL is a C++ template library shipped with the Windows SDK 8 and 8.1. It allows to build WinRT components.
WinRT components are used in Windows Store Apps.
To implement async methods, Microsoft provides you a complete set of extensions to use with C++ extensions called C++/CX and PPL Taks.
If you want to use Standard ISO C++ you have to investigate into the WRL include files and its inner C++ templates.
There are no samples. The MSDN documentation describes some classes like AsyncBase but you have to provide your own plumbing around it.
Let's begin with a basic WRL project. We will provide a Root component wich can create a Logger component the async way.
Here is the IDL file:
import "inspectable.idl"; import "Windows.Foundation.idl"; #define COMPONENT_VERSION 1.0 namespace Library1 { interface ILogger; runtimeclass Logger; [uuid(3EC4B4D6-14A6-4D0D-BB96-31DA25224A15), version(COMPONENT_VERSION)] interface ILogger : IInspectable { HRESULT LogInfo([in] HSTRING value); } [version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)] runtimeclass Logger { [default] interface ILogger; } interface IRoot; runtimeclass Root; [uuid(3EC4B4D6-14A6-4D0D-BB96-31DA25224A16), version(COMPONENT_VERSION)] interface IRoot : IInspectable { HRESULT GetLoggerAsync([out][retval] Windows.Foundation.IAsyncOperation<ILogger*>** value); } [version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)] runtimeclass Root { [default] interface IRoot; } }
The method named GetLoggerAsync will return an IAsyncOperation<ILogger*> type.
Here is the header file for the Root component:
#pragma once #include "Library1_h.h" namespace ABI { namespace Library1 { class Root : public RuntimeClass<IRoot> { InspectableClass(L"Library1.Root", BaseTrust) public: Root(); public: STDMETHOD(GetLoggerAsync)(Windows::Foundation::IAsyncOperation<ILogger*>** value); }; ActivatableClass(Root); } }
Now we need to provide an implementation plumbing for IAsyncOperation. We need to provide a class that inherits from WRL AsyncBase class.
If have searched for a sample but Microsoft does not provide any one...
A stackoverflow thread about WRL and async topic talks about the CXXReflect library.
I look at this project and yes, I found a class which inherits from AsyncBase. Here is the extract:
#pragma once #include "pch.h" // // Utility header extracted from CXXReflect\Windows_Runtime\Utility.hpp // // Copyright James P. McNellis 2011 - 2013. // // Distributed under the Boost Software License, Version 1.0. // /// A base class for IAsyncOperation<T> implementations for use with WRL template <typename T> class async_operation_base : public ::Microsoft::WRL::RuntimeClass< ::Microsoft::WRL::AsyncBase< ::ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T*>>, ::ABI::Windows::Foundation::IAsyncOperation<T*> > { InspectableClass(L"Windows.Foundation.IAsyncInfo", BaseTrust) public: typedef ::ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T*> HandlerType; async_operation_base() { this->Start(); } virtual auto STDMETHODCALLTYPE put_Completed(HandlerType* handler) -> HRESULT override { return this->PutOnComplete(handler); } virtual auto STDMETHODCALLTYPE get_Completed(HandlerType** handler) -> HRESULT override { return this->GetOnComplete(handler); } protected: virtual auto OnStart() -> HRESULT override { return S_OK; } virtual auto OnClose() -> void override { }; virtual auto OnCancel() -> void override { }; }; /// An IAsyncOperation<T> implementation that returns an already-realized value template <typename T> class already_completed_async_operation : public async_operation_base<T> { public: already_completed_async_operation(T* const value) : _value(value) { this->FireCompletion(); } virtual auto STDMETHODCALLTYPE GetResults(T** results) -> HRESULT override { if (results == nullptr) return E_INVALIDARG; *results = _value.Get(); return S_OK; } private: ::Microsoft::WRL::ComPtr<T> _value; }; /// An IAsyncOperation<T> implementation that waits on a std::future using PPL template <typename T> class task_based_async_operation : public async_operation_base<T> { public: task_based_async_operation(std::future<T*>&& f) : _future(std::move(f)), _task([&]() -> ::Microsoft::WRL::ComPtr<T> { return _future.get(); }) { _task.then([&](::Microsoft::WRL::ComPtr<T>) { this->FireCompletion(); }); } virtual auto STDMETHODCALLTYPE GetResults(T** results) -> HRESULT override { if (results == nullptr) return E_INVALIDARG; *results = clone_for_return(_task.get()); return S_OK; } private: std::future<T*> _future; ::Concurrency::task<::Microsoft::WRL::ComPtr<T>> _task; }; /// A helper to AddRef an interface pointer for return to a caller. template <typename T> auto clone_for_return(::Microsoft::WRL::ComPtr<T> p) -> T* { return p.Detach(); }
So we will use this header to provide a helper for our async method.
Here is the body file for the Root component:
#include "pch.h" #include "Root.h" #include "Utility.h" #include "Logger.h" namespace ABI { namespace Library1 { Root::Root() { } STDMETHODIMP Root::GetLoggerAsync(Windows::Foundation::IAsyncOperation<ILogger*>** value) { *value = Make<task_based_async_operation<ILogger>>(std::async([&]() -> ILogger* { ComPtr<Logger> p = Make<Logger>(); return p.Detach(); })).Detach(); return S_OK; } } }
You can see that we just use the task_based_async_operation template class. It takes a future provided by std::async().
We just have to create the WinRT component Logger using ComPtr and Make function.
All of this is executed in a concurrency task. Just look again at the utility header and you will find the task operation.
Now we have built our WinRT component, we can use it in a XAML C# App for example.
We just create a simple XAML C# App and add a button. Here the code to call the Root component.
You will notice that the using directive is using Library1. It does not contains the ABI namespace.
using Library1; namespace App1 { /// <summary> /// An empty page that can be used on its own or navigated to within a Frame. /// </summary> public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { try { Library1.Logger l = new Library1.Logger(); l.LogInfo("this is a log"); Library1.Root root = new Root(); Library1.ILogger ilogger = await root.GetLoggerAsync(); ilogger.LogInfo("log me !"); } catch(Exception ex) { ... } } } }
The entire source code of this article is available on the Windows Dev Center: WRL Sample for Vector and Async