How to: Complete Asynchronous Operations Using WRL
This document shows how to use the Windows Runtime C++ Template Library (WRL) to start asynchronous operations and perform work when the operations complete.
This document shows two examples. The first example starts an asynchronous timer and waits for the timer to expire. In this example, you specify the asynchronous action when you create the timer object. The second example runs a background worker thread. This example shows how to work with a Windows Runtime method that returns an IAsyncInfo
interface. The Callback function is an important part of both examples because it enables them to specify an event handler to process the results of the asynchronous operations.
For a more basic example that creates an instance of a component and retrieves a property value, see How to: Activate and Use a Windows Runtime Component.
Tip
These examples use lambda expressions to define the callbacks. You can also use function objects (functors), function pointers, or std::function objects. For more information about C++ lambda expressions, see Lambda Expressions.
Example: Working with a Timer
The following steps start an asynchronous timer and wait for the timer to expire. The complete example follows.
Warning
Although you typically use the Windows Runtime C++ Template Library in a Universal Windows Platform (UWP) app, this example uses a console app for illustration. Functions such as wprintf_s
are not available from a UWP app. For more information about the types and functions that you can use in a UWP app, see CRT functions not supported in Universal Windows Platform apps and Win32 and COM for UWP apps.
Include (
#include
) any required Windows Runtime, Windows Runtime C++ Template Library, or C++ Standard Library headers.#include <Windows.Foundation.h> #include <Windows.System.Threading.h> #include <wrl/event.h> #include <stdio.h> #include <Objbase.h> using namespace ABI::Windows::Foundation; using namespace ABI::Windows::System::Threading; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers;
Windows.System.Threading.h
declares the types that are required to use an asynchronous timer.We recommend that you utilize the
using namespace
directive in your .cpp file to make the code more readable.Initialize the Windows Runtime.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
Create an activation factory for the
ABI::Windows::System::Threading::IThreadPoolTimer
interface.// Get the activation factory for the IThreadPoolTimer interface. ComPtr<IThreadPoolTimerStatics> timerFactory; HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
The Windows Runtime uses fully-qualified names to identify types. The
RuntimeClass_Windows_System_Threading_ThreadPoolTimer
parameter is a string that's provided by the Windows Runtime and contains the required runtime class name.Create an Event object that synchronizes the timer callback to the main app.
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete. // This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete. Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS)); hr = timerCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError()); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
Note
This event is for demonstration only as part of a console app. This example uses the event to ensure that an async operation completes before the app exits. In most apps, you typically don't wait for async operations to complete.
Create an
IThreadPoolTimer
object that expires after two seconds. Use theCallback
function to create the event handler (anABI::Windows::System::Threading::ITimerElapsedHandler
object).// Create a timer that prints a message after 2 seconds. TimeSpan delay; delay.Duration = 20000000; // 2 seconds. auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT { wprintf_s(L"Timer fired.\n"); TimeSpan delay; HRESULT hr = timer->get_Delay(&delay); if (SUCCEEDED(hr)) { wprintf_s(L"Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0); } // Set the completion event and return. SetEvent(timerCompleted.Get()); return hr; }); hr = callback ? S_OK : E_OUTOFMEMORY; if (FAILED(hr)) { return PrintError(__LINE__, hr); } ComPtr<IThreadPoolTimer> timer; hr = timerFactory->CreateTimer(callback.Get(), delay, &timer); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
Print a message to the console and wait for the timer callback to complete. All
ComPtr
and RAII objects leave scope and are released automatically.// Print a message and wait for the timer callback to complete. wprintf_s(L"Timer started.\nWaiting for timer...\n"); // Wait for the timer to complete. WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE); // All smart pointers and RAII objects go out of scope here.
Here is the complete example:
// wrl-consume-async.cpp
// compile with: runtimeobject.lib
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
return hr;
}
int wmain()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
if (FAILED(initialize))
{
return PrintError(__LINE__, initialize);
}
// Get the activation factory for the IThreadPoolTimer interface.
ComPtr<IThreadPoolTimerStatics> timerFactory;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete.
// This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
hr = timerCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Create a timer that prints a message after 2 seconds.
TimeSpan delay;
delay.Duration = 20000000; // 2 seconds.
auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT
{
wprintf_s(L"Timer fired.\n");
TimeSpan delay;
HRESULT hr = timer->get_Delay(&delay);
if (SUCCEEDED(hr))
{
wprintf_s(L"Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0);
}
// Set the completion event and return.
SetEvent(timerCompleted.Get());
return hr;
});
hr = callback ? S_OK : E_OUTOFMEMORY;
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
ComPtr<IThreadPoolTimer> timer;
hr = timerFactory->CreateTimer(callback.Get(), delay, &timer);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Print a message and wait for the timer callback to complete.
wprintf_s(L"Timer started.\nWaiting for timer...\n");
// Wait for the timer to complete.
WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE);
// All smart pointers and RAII objects go out of scope here.
}
/*
Output:
Timer started.
Waiting for timer...
Timer fired.
Timer duration: 2.00 seconds.
*/
Compiling the Code
To compile the code, copy it and then paste it in a Visual Studio project, or paste it in a file that is named wrl-consume-async.cpp
and then run the following command in a Visual Studio Command Prompt window.
cl.exe wrl-consume-async.cpp runtimeobject.lib
Example: Working with a Background Thread
The following steps start a worker thread and define the action that's performed by that thread. The complete example follows.
Tip
This example demonstrates how to work with the ABI::Windows::Foundation::IAsyncAction
interface. You can apply this pattern to any interface that implements IAsyncInfo
: IAsyncAction
, IAsyncActionWithProgress
, IAsyncOperation
, and IAsyncOperationWithProgress
.
Include (
#include
) any required Windows Runtime, Windows Runtime C++ Template Library, or C++ Standard Library headers.#include <Windows.Foundation.h> #include <Windows.System.Threading.h> #include <wrl/event.h> #include <stdio.h> #include <Objbase.h> using namespace ABI::Windows::Foundation; using namespace ABI::Windows::System::Threading; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers;
Windows.System.Threading.h declares the types that are required to use a worker thread.
We recommend that you use the
using namespace
directive in your .cpp file to make the code more readable.Initialize the Windows Runtime.
// Initialize the Windows Runtime. RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); if (FAILED(initialize)) { return PrintError(__LINE__, initialize); }
Create an activation factory for the
ABI::Windows::System::Threading::IThreadPoolStatics
interface.// Get the activation factory for the IThreadPoolStatics interface. ComPtr<IThreadPoolStatics> threadPool; HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
Create an Event object that synchronizes completion of the worker thread to the main app.
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete. // This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete. Event threadCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS)); hr = threadCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError()); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
Note
This event is for demonstration only as part of a console app. This example uses the event to ensure that an async operation completes before the app exits. In most apps, you typically don't wait for async operations to complete.
Call the
IThreadPoolStatics::RunAsync
method to create a worker thread. Use theCallback
function to define the action.wprintf_s(L"Starting thread...\n"); // Create a thread that computes prime numbers. ComPtr<IAsyncAction> asyncAction; hr = threadPool->RunAsync(Callback<IWorkItemHandler>([&threadCompleted](IAsyncAction* asyncAction) -> HRESULT { // Print a message. const unsigned int start = 0; const unsigned int end = 100000; unsigned int primeCount = 0; for (int n = start; n < end; n++) { if (IsPrime(n)) { primeCount++; } } wprintf_s(L"There are %u prime numbers from %u to %u.\n", primeCount, start, end); // Set the completion event and return. SetEvent(threadCompleted.Get()); return S_OK; }).Get(), &asyncAction); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
The
IsPrime
function is defined in the complete example that follows.Print a message to the console and wait for the thread to complete. All
ComPtr
and RAII objects leave scope and are released automatically.// Print a message and wait for the thread to complete. wprintf_s(L"Waiting for thread...\n"); // Wait for the thread to complete. WaitForSingleObjectEx(threadCompleted.Get(), INFINITE, FALSE); wprintf_s(L"Finished.\n"); // All smart pointers and RAII objects go out of scope here.
Here is the complete example:
// wrl-consume-asyncOp.cpp
// compile with: runtimeobject.lib
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
return hr;
}
// Determines whether the input value is prime.
bool IsPrime(int n)
{
if (n < 2)
{
return false;
}
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
{
return false;
}
}
return true;
}
int wmain()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
if (FAILED(initialize))
{
return PrintError(__LINE__, initialize);
}
// Get the activation factory for the IThreadPoolStatics interface.
ComPtr<IThreadPoolStatics> threadPool;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete.
// This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
Event threadCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
hr = threadCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
wprintf_s(L"Starting thread...\n");
// Create a thread that computes prime numbers.
ComPtr<IAsyncAction> asyncAction;
hr = threadPool->RunAsync(Callback<IWorkItemHandler>([&threadCompleted](IAsyncAction* asyncAction) -> HRESULT
{
// Print a message.
const unsigned int start = 0;
const unsigned int end = 100000;
unsigned int primeCount = 0;
for (int n = start; n < end; n++)
{
if (IsPrime(n))
{
primeCount++;
}
}
wprintf_s(L"There are %u prime numbers from %u to %u.\n", primeCount, start, end);
// Set the completion event and return.
SetEvent(threadCompleted.Get());
return S_OK;
}).Get(), &asyncAction);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
// Print a message and wait for the thread to complete.
wprintf_s(L"Waiting for thread...\n");
// Wait for the thread to complete.
WaitForSingleObjectEx(threadCompleted.Get(), INFINITE, FALSE);
wprintf_s(L"Finished.\n");
// All smart pointers and RAII objects go out of scope here.
}
/*
Output:
Starting thread...
Waiting for thread...
There are 9592 prime numbers from 0 to 100000.
Finished.
*/
Compiling the Code
To compile the code, copy it and then paste it in a Visual Studio project, or paste it in a file that is named wrl-consume-asyncOp.cpp
and then run the following command in a Visual Studio Command Prompt window.
cl.exe wrl-consume-asyncOp.cpp runtimeobject.lib