Skip to content

WinRT and COM wrappers internals

James Pack edited this page Feb 11, 2023 · 3 revisions

WinRT and COM wrappers internals

This page describes the internals of the WinRT and COM wrappers. The internals are subject to change at any time. They are described here to aid in debugging and to provide guidance to future WIL implementors.

copy and query tags

// wil::details namespace

typedef wistd::integral_constant<char, 0> tag_com_query;
typedef wistd::integral_constant<char, 1> tag_try_com_query;
typedef wistd::integral_constant<char, 2> tag_com_copy;
typedef wistd::integral_constant<char, 3> tag_try_com_copy;

These are marker types passed to the com_ptr_t constructor to indicate how the constructor should treat the constructor parameter.

Tag Purpose What happens
wil::details::tag_com_query Construct for query Throw or fail-fast if the query fails. (Not used by return-code policy.)
wil::details::tag_try_com_query Construct for try_query Construct empty wrapper if the query fails.
wil::details::tag_com_copy Construct for copy Throw or fail-fast if the copy fails. Construct empty wrapper if the source is null.
wil::details::tag_try_com_copy Construct for try_copy Construct empty wrapper if the copy fails or the source is null.

is_com_convertible

// wil::details namespace

template<class TFrom, class TTo>
struct is_com_convertible;

A type-traits class that behaves the same as std::is_convertible, but allows ambiguous conversions if the TFrom is a COM interface.

(COM interfaces are detected by checking if they are abstract.)

The implementation takes advantage of a quirk of MSVC in which the __is_convertible_to intrinsic erroneously reports true for an ambiguous conversion. This bug has been fixed in MSVC, so is_com_convertible now returns false for ambiguous COM base classes.

This is not an issue in practice because COM interfaces cannot have multiple base classes. Implementations can have multiple base classes, but it is proper to reject the ambiguous conversion in that case, because there is no way to say "Give me a pointer to any of the compatible base classes, I don't care which one."

Query policies

The implementation of the query and copy method families relies upon a "query policy" object, a traits type which describes how a COM pointer is converted to other COM pointers. The query policy must support the following two methods:

template<typename T>
static HRESULT query(T* ptr, REFIID riid, void** result);

template<typename T, typename TResult>
static HRESULT query(T* ptr, TResult** result);

The functions query the ptr (which will not be null) for the target interface, either specified at runtime (riid) or compile-time (__uuidof(*result)).

On failure, *result is set to nullptr.

To obtain the appropriate query policy for a type, use the query_policy_t template type:

// wil::details namespace

template <typename T>
struct query_policy_helper
{
    typedef default_query_policy type; // see below
};

template <typename T>
using query_policy_t = typename query_policy_helper<typename wistd::remove_pointer<T>::type>::type;

The template type parameter to query_policy_t can be a COM interface or a pointer to a COM interface.

To define a custom query policy, specialize the query_policy_helper template.

Default query policy

The default query policy performs the a QueryInterface to obtain the result from the source.

In the special case where TResult is a COM base class of T, the QueryInterface is optimized out by simply copying the raw pointer and performing an AddRef.

The optimization is performed by calling an overloaded helper function with the result of is_com_convertible.

By default, all COM pointers use the default query policy.

Weak query policy

The query_policy_helper is specialized so that the weak query policy is used for IWeakReference.

The weak query policy resolves the weak pointer, and performs the query on the resolved pointer. If either step fails, then the weak query fails.

You cannot query the weak query policy for IWeakReference itself.

In the special case where TResult is IInspectable, the second QueryInterface is optimized out. Again, the optimization is performed by calling an overloaded helper function.

Agile query policy

The query_policy_helper is specialized so that the agile query policy is used for IAgileReference.

The agile query policy resolves the agile reference to desired result interface.

You cannot query the agile query policy for IAgileReference itself.