Skip to content

WIL and CppWinRT together

Duncan Horn edited this page Mar 21, 2024 · 1 revision

WIL and C++/WinRT are largely independent libraries which complement each other, but they can integrate at a few points.

In versions of C++/WinRT prior to 2.0, WIL's integration with C++/WinRT requires that wil/cppwinrt.h, be included before any C++/WinRT header files:

// good - WIL first
#include <wil/cppwinrt.h>
#include <winrt/base.h>

// bad - C++/WinRT first;
// some integration features may not be turned on in C++/WinRT versions less than 2.0
#include <winrt/base.h>
#include <wil/cppwinrt.h>

Integration features

capture_interop

wil::capture_interop extends winrt::capture for the specific case of Windows Runtime interop interfaces.

It takes two forms, depending on whether you are capturing from a factory interop interface or from an instance interop interface.

Factory form

template<typename WinRTResult, typename WinRTFactory = WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args);

Most of the template parameters are defaulted or deduced. Only the WinRTResult is mandatory.

  • The WinRTResult is the type of the object you are trying to obtain.
  • The WinRTFactory is the type of the factory from which to get it. It defaults to the WinRTResult.
  • The remaining types are deduced from the member function pointer.

The factory-form capture_interop method does the following:

  • Creates the factory object corresponding to WinRTFactory.
  • Queries it for the interface whose member function pointer you passed as a parameter.
  • Calls the method you passed as a parameter, with the optional arguments you passed as the subsequent parameters.
  • Captures the result into an object of type WinRTResult and returns it.

Capturing from a factory interop interface corresponds to calling a Windows Runtime static method. For example, the InputPane Windows Runtime class factory supports this method

HRESULT IInputPaneInterop::GetForWindow(HWND hwnd, REFIID riid, void** ppv);

to allow an InputPane to be produced from a Win32 window handle. You can use it with capture_interop like this:

winrt::InputPane inputPane = wil::capture_interop<winrt::InputPane>(&IInputPaneInterop::GetForWindow, hwnd);

If the method's output is different from the factory type, specify the factory type as the second template parameter.

winrt::IAsyncAction action = wil::capture_interop<winrt::IAsyncAction, winrt::AccountsSettingsPane>
                              (&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd);

Instance form

 template<typename WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
 auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)

Most of the template parameters are defaulted or deduced. Only the WinRTResult is mandatory.

  • The WinRTResult is the type of the object you are trying to obtain.
  • The remaining types are deduced from the member function pointer.

The instance-form capture_interop method does the following:

  • Queries the first parameter for the interface whose member function pointer you passed as a parameter.
  • Calls the method you passed as the second parameter, with the optional arguments you passed as the subsequent parameters.
  • Captures the result into an object of type WinRTResult and returns it.

Capturing from an instance interop interface corresponds to calling a Windows Runtime instance method.

For example, the UserActivitySession Windows Runtime object supports this method

IUserActivityInterop::CreateSessionForWindow(HWND hwnd, REFIID riid, void** ppv);

to create a UserActivitySession from a UserActivity given a window handle. You can use it with capture_interop like this:

winrt::UserActivity activity = /* initialize from somewhere */;
winrt::UserActivitySession session = wil::capture_interop<winrt::UserActivitySession>
                                         (activity, &IUserActivityInterop::CreateSessionForWindow, hwnd);

resume_foreground

The built-in C++/WinRT winrt::resume_foreground function contains a number of defects. WIL provides a replacement that avoids many of the pitfalls.

The WIL version throws the winrt::hresult_error(HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)) exception if it detects that the coroutine failed to resume on the dispatcher. (The C++/WinRT version either hangs the coroutine or resumes execution on the wrong thread.)

The WIL version supports the following dispatchers:

  • Windows.UI.Core.CoreDispatcher
  • Windows.System.DispatcherQueue
  • Microsoft.System.DispatcherQueue
  • Microsoft.UI.Dispatching.DispatcherQueue

Support for other dispatchers can be added by specializing the dispatcher_traits traits type.

winrt_conditionally_implements

This template class provides boilerplate for implementing a C++/WinRT object with conditional support for an interface.

Assume the existence of a class "Version2" which says whether the IMyThing2 interface should be supported:

struct Version2 { static bool IsEnabled(); };

You can wrap your winrt::ClassT or winrt::implements inside the wil::winrt_conditionally_implements and provide pairs of detectors and interfaces.

struct MyThing : wil::winrt_conditionally_implements<
    MyThingT<MyThing>,
    Version2, IMyThing2>
{
    // implementation goes here
};

struct AnotherThing : wil::winrt_conditionally_implements<
    winrt::implements<AnotherThing, IMyThing, IMyThing2>,
    Version2, IMyThing2>
{
    // implementation goes here
};

In the above examples, if Version2::IsEnabled() returns false, then the QueryInterface for IMyThing2 will fail.

Any interface not listed as conditional is assumed to be enabled unconditionally.

You can add additional Version / Interface pairs to the template parameter list. Interfaces may be conditionalized on at most one Version class. If you need a complex conditional, create a new helper class.

// Helper class for testing two Versions.
struct Version2_or_greater {
    static bool IsEnabled() { return Version2::IsEnabled() || Version3::IsEnabled(); }
};

// This implementation supports IMyThing2 if either Version2 or Version3 is enabled,
// and supports IMyThing3 only if Version3 is enabled.
struct MyThing : wil::winrt_conditionally_implements<MyThingT<MyThing>,
    Version2_or_greater, IMyThing2, Version3, IMyThing3>
{
    // implementation goes here
};