-
Notifications
You must be signed in to change notification settings - Fork 235
WIL and CppWinRT together
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>
-
WIL and C++/WinRT can understand each other's exceptions, so that a WIL exception can be caught by C++/WinRT code and vice versa. If you do not enable this, then each library will treat the other library's exceptions as "foreign". WIL fails fast on foreign exceptions. C++/WinRT converts all foreign exceptions to
E_FAIL
. -
WIL's error handling helpers can produce more useful log messages for uncaught C++/WinRT projected exceptions.
-
The
unknwn.h
Windows SDK header file is included, so C++/WinRT will turn on its support for consuming and implementing classic COM interfaces.-
inspectable.h
from the SDK is also included, so C++/WinRT will turn on its support for implementing classic COM interfaces that nevertheless inherit from IInspectable, the parent of all Windows Runtime interfaces.
-
-
WIL provides some small helper functions for converting between C++/WinRT projection types and Windows Runtime ABI types:
- Wrappers for
winrt::put_abi
andwinrt::get_abi
that return strongly typed ABI pointers rather thanvoid *
. -
wil::convert_from_abi
converts from an ABI-typed pointer to the equiavlent C++/WinRT projection object, just like theconvert_from_abi
example function on docs.microsoft.com.
- Wrappers for
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.
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 theWinRTResult
. - 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);
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);
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.
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
};