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
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 { try { // Check for error and wrap in platfrom exception if (FAILED(error)) { throw Platform::Exception::CreateException(error); } } catch(...) { // Pass exception on to calling code using the // Task Completion Event's dedicated method tce.set_exception(std::current_exception()); return error; } // Set the Task Completion Event's Result to mark the Task as complete // If a IAsyncOperationis used the result value is passed tce.set(); 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