Friday, December 20, 2013

What are you Awaiting for - How to Turn a C/C++ Callback into a C++/Cx WinRT IAsyncAction

Windows and Windows Phone 8 and its WinRT / WinPRT component model provide a nice way to leverage good ol' native C/C++ libraries in all WinMD language projections by wrapping them in a WinRT C++/Cx component. Such a native WinRT component only exposes WinRT types and can therefore be consumed through the WinMD projections also in C# or WinJS.
Often those good ol' native libraries provide events or callback functions for asynchronous calls. This means one would also have to provide a WinRT wrapper for those events and callbacks as well, although the WinRT has the nice IAsyncAction and IAsyncOperation constructs together with the Parallel Patterns Library (PPL) which make the WinRT component way better to use.
This post describes how to plug both worlds together to turn your good ol' C++ code into rose colored C++/Cx consumable through WinMD.

We won't go too deep into C++/Cx in general here. There's a good intro at the MSDN and also a nice Quick Reference which provides a table how Standard C++ types and constructs compare to C++/Cx. I also don't cover how the PPL and WinRT async works here, but there's a great MSDN page: Creating Asynchronous Operations in C++ for Windows Store Apps.

Assuming you have a good ol' native interface like this with a defined callback template:
template <typename TCallback>
HRESULT StartAsyncOperation(const TCallback &callback)

It could be used like this using a C++11 lambda as callback function:
StartAsyncOperation([this](HRESULT error) -> HRESULT
   // Do whatever here after the async is done...
   // Notify the calling code through an event or another callback 
   // that the async operation has finished.
   return S_OK;

It's worth mentioning this will also work with C function pointers and you can use a C++ 11 lambda for it as long as the lambda does not capture any variable. Stateless lambdas will just degrade to function pointers.
// Definition of fucntion pointer and a function expecting it as parameter
typedef void (*SomeOtherCallback)(int param1);

FunctionExpectingSomeOtherCallback(SomeOtherCallback cb);

// Example call using a stateless lambda
FunctionExpectingSomeOtherCallback([](int param1)
   // Perform some operation with param1

This is how your C++/Cx WinRT component's method which wraps the above C++ template could look like:
Windows::Foundation::IAsyncAction^ MyWinRtComponent::RunAsync()
 // Setup  the Task Completion Event and the async Task
 Concurrency::task_completion_event<void> tce;
 auto tsk = Concurrency::create_task(tce);
 Windows::Foundation::IAsyncAction^ asyncOp = Concurrency::create_async( [tsk]() 
            -> Concurrency::task<void> 
  return tsk;

 // Run good ol' native code wih callback defined as C++ lambda
 StartAsyncOperation([this, tce](HRESULT error) -> HRESULT
   // Check for error and wrap in platfrom exception
   if (FAILED(error))
    throw Platform::Exception::CreateException(error);
   // Pass exception on to calling code using the 
   // Task Completion Event's dedicated method
   return error;

  // Set the Task Completion Event's Result to mark the Task as complete
  // If a IAsyncOperation is used the result value is passed

  return S_OK;

 return asyncOp;

You could then use your WinRT component in C# like this:
var component = new MyWinRtComponent();
await component.RunAsync();

And similar in WinJS using a JS Promise:
this._component.runAsync().then(function () {
 WinJS.log("My component finished the async call! :-)", null, "status");

Way better API for the custom WinRT component if you ask me!
The trick to achieve that is the usage of the PPL's create_async, create_task and especially the task_completion_event which provides a nice way to make that callback-based model work with the proper WinRT modern async Task-based way.

No comments:

Post a Comment