-
Notifications
You must be signed in to change notification settings - Fork 239
WinRT and COM wrappers
WIL WinRT and COM
wrappers help you use COM pointers safely, just as though they were
std
smart pointers like std::unique_ptr
.
The COM wrappers can be used by including the correct header file:
#include <wil/com.h>
Similar to WIL's
resource wrappers, the COM wrappers
you are expected to use are based off of a template class,
wil::com_ptr_t<T>
, which serves as the basis for the WIL COM wrappers.
You should not need to instantiate this class directly. Instead, use
one of the Aliases defined below.
template <typename T,
typename err_policy = err_exception_policy>
class com_ptr_t;
-
T
The underlying type being managed by thecom_ptr_t
. This is usually the name of a COM interface (no trailing*
). Note that the behavior of the wrapper class is slightly different ifT
isIAgileReference
orIWeakReference
. See com_agile_ref and com_weak_ref below for details. -
err_policy
The error handling policy. See the aliases below.
The following aliases have been defined for the common error handling policies.
Alias | Policy | Description |
---|---|---|
com_ptr<T> |
err_exception_policy |
Throws exceptions on failure. |
com_ptr_nothrow<T> |
err_returncode_policy |
Returns HRESULT values to indicate failure. |
com_ptr_failfast<T> |
err_failfast_policy |
Terminates with a fast-fail on failure. |
-
result
The function return result for the provided error policy. This isvoid
for exceptions and fail-fast policies, andHRESULT
for the return-code policy. -
element_type
The template typeT
being held by thecom_ptr_t
. -
pointer
A pointer to the template typeT
being held by thecom_ptr_t
. In other words, it isT*
.
-
com_ptr_t()
(default constructor)
Constructs an empty wrapper. -
com_ptr_t(nullptr)
Constructs an empty wrapper. -
com_ptr_t(T* ptr)
Constructs an additional reference to the COM object represented by the raw pointer. Ifptr
isnullptr
, then the constructed wrapper is empty. -
com_ptr_t(const com_ptr_t<...>& other)
and
com_ptr_t(const Microsoft::WRL::ComPtr<...>& other)
(copy constructor)
Constructs an additional reference to the COM object represented byother
. If theother
is empty, then the constructed wrapper is also empty. Theother
can be any kind ofcom_ptr_t
orComPtr
, provided the underlying element of the source is compatible with the target. For example, you can construct acom_ptr_nothrow<IUnknown>
from acom_ptr_failfast<IStream>
becauseIStream*
can be converted toIUnknown*
. The error handling policies do not need to match. -
com_ptr_t(com_ptr_t<...>&& other)
and
com_ptr_t(Microsoft::WRL::ComPtr<...>&& other)
(move constructor)
Transfers ownership of the COM object fromother
to the constructed wrapper. Theother
can be any kind ofcom_ptr_t
orComPtr
, provided the underlying element of the source is compatible with the target.
-
~com_ptr_t
Releases the wrapped COM object.
-
T* get() const
Returns the wrapped COM object, ornullptr
if the wrapper is empty. -
void reset()
Release the current COM object and leaves the wrapper empty. -
void reset(nullptr)
Release the current COM object and leaves the wrapper empty.
Note that there is no reset(T* ptr)
. Use the assignment operator
instead.
-
void attach(T* ptr)
Release the current COM object and takes ownership of the provided raw pointer. Ifptr
isnullptr
, then the wrapper is left empty. -
T* detach()
Returns the current COM object and makes the wrapper empty. It is the caller's responsibility to release the returned raw pointer if non-null. -
T** addressof()
Returns the address of the internal pointer without releasing the current COM object. Do not use this for_Out_
parameters because it will leak the current COM object. For_Out_
parameters, use the&
operator, which releases the current COM object before returning the address. -
T* const* addressof() const
Const version of above. The returned pointer cannot be modified. -
T** put()
Releases the current COM object and returns the address of the internal pointer. Use this method when passing thecom_ptr_t
as a_Out_
parameter. -
T** operator&()
Same asput()
. -
void** put_void()
Same asput()
, but returns the address as avoid**
. -
IUnknown** put_unknown()
Same asput()
, but returns the address as aIUnknown**
. -
swap(com_ptr_t<T, ...>& other)
and
swap(Microsoft::WRL::ComPtr<T>& other)
Swap pointers with another wrapper. The wrappers must wrap the same COM interface, but they can have different error policies. Also available as a free function for ADL purposes. -
swap(com_ptr_t<T, ...>&& other)
and
swap(Microsoft::WRL::ComPtr<T>&& other)
(rvalue reference)
Swap pointers with another wrapper. The wrappers must wrap the same COM interface, but they can have different error policies.
There are two families of conversion functions, query
and copy
.
The query
methods require that the wrapper be non-empty. If the
wrapper is empty, the behavior is undefined. (Usually, you will crash on
a null pointer.)
The copy
methods permit the wrapper to be empty, in which case the
result is also empty.
The try_
prefix means that the function may fail. On failure, it
produces an empty wrapper, and returns false
(if applicable).
Both the query
and copy
methods optimize the case where the source
COM type is the same as or can be converted to the destination COM type,
in which case it just copies the pointer calling AddRef
and avoids the
call to QueryInterface
.
All of the query
and copy
methods are const
.
Direct | Try | |
---|---|---|
com_ptr_t<U> query<U>() com_ptr_t<U> copy<U>() see Notes 1 and 2 below |
com_ptr_t<U> try_query<U>() com_ptr_t<U> try_copy<U>() see Note 2 below |
|
result query_to(U** p) result copy_to(U** p)
|
bool try_query_to(U** p) bool try_copy_to(U** p)
|
|
result query_to(REFIID riid, void** ppv) result copy_to(REFIID riid, void** ppv)
|
bool try_query_to(REFIID riid, void** ppv) bool try_copy_to(REFIID riid, void** ppv)
|
|
Reports failure according to the error policy. | Returns an empty wrapper or false on failure. |
- Note 1:
query
andcopy
cannot be used with return-code policy. - Note 2: (
try_
)query
and (try_
)copy
produce acom_ptr_t
with the same error policy as the original wrapper.
Typical usages for exception-based and fail-fast-based wrappers:
// throughout, com_ptr_failfast can be used in place of com_ptr
wil::com_ptr<IBaz> TryGetInnerBaz()
{
wil::com_ptr<IBar> bar = ...;
// Requires that bar be non-empty.
auto foo = bar.query<IFoo>();
auto baz = foo->TryGetBaz();
return baz;
}
// The above could be simplified to
wil::com_ptr<IBaz> TryGetInnerBaz()
{
wil::com_ptr<IBar> bar = ...;
// Requires that bar be non-empty.
return bar.query<IFoo>()->TryGetBaz();
}
void Example()
{
wil::com_ptr<IBar> bar = ...;
auto foo = bar.try_query<IFoo>();
if (foo)
{
foo->Method();
}
}
Typical usages for return-code-based wrappers:
HRESULT TryGetInnerBaz(_COM_Outptr_ IBaz** result)
{
*result = nullptr;
wil::com_ptr_nothrow<IBar> bar = ...;
// Requires that bar be non-empty.
wil::com_ptr_nothrow<IFoo> foo;
RETURN_IF_FAILED(bar.query_to(&foo));
wil::com_ptr_nothrow<IBaz> baz;
RETURN_IF_FAILED(foo->TryGetBaz(&baz));
// Use copy_to because baz might be empty.
RETURN_IF_FAILED(baz.copy_to(result));
return S_OK;
}
HRESULT Example()
{
wil::com_ptr_nothrow<IBar> bar = ...;
auto foo = bar.try_query<IFoo>();
if (foo)
{
foo->Method();
}
wil::com_ptr_nothrow<IBaz> baz;
RETURN_IF_FAILED(bar.try_query_to(&baz));
baz->Method();
return S_OK;
}
-
operator=(nullptr)
Releases the current COM object and leaves the wrapper empty. -
operator=(T* ptr)
Releases the current COM object and creates an additional reference to the COM object represented by the raw pointer. -
operator=(const com_ptr_t<...>& other)
and
operator=(const Microsoft::WRL::ComPtr<...>& other)
Releases the current COM object and creates an additional reference to the COM object represented byother
. Theother
can be any kind ofcom_ptr_t
orComPtr
, provided the underlying element of the source is compatible with the target. For example, you can construct acom_ptr_nothrow<IUnknown>
from acom_ptr_failfast<IStream>
becauseIStream*
can be converted toIUnknown*
. The error handling policies do not need to match. -
operator=(com_ptr_t<...>&& other)
and
operator=(Microsoft::WRL::ComPtr<...>&& other)
(move assignment)
Releases the current COM object and transfers ownership of the COM object fromother
to the wrapper. Theother
can be any kind ofcom_ptr_t
orComPtr
, provided the underlying element of the source is compatible with the target. -
T** operator&()
Releases the current COM object and returns the address of the internal pointer. To obtain the address of the internal pointer without releasing the COM object (in the rare case of an_Inout_
parameter), use theaddressof
method. -
operator bool()
Returnstrue
if there is a current COM object, orfalse
if the wrapper is empty. -
T* operator->() const
Returns the wrapped COM object pointer. This crashes if the wrapper is empty. -
T& operator*() const
Dereferences the wrapped COM object. This crashes if the wrapper is empty. -
==
,!=
,<
,<=
,>
,>=
comparison operators
These perform comparison on the underlying raw pointers. The left and right sides can have different error policies, and the underlying pointers need only be convertible. For example, you can compare acom_ptr<IUnknown>
with aComPtr<IStream>
. You can also compare for equality and inequality with raw pointers and withnullptr
.
Starting in C++20, you can use class template argument deduction
for alias templates to construct a com_ptr_t
from a raw pointer
without having to repeat the type name.
For earlier versions of C++, WIL provides
the functions wil::make_com_ptr
, wil::make_com_ptr_nothrow
and
wil::make_com_ptr_failfast
which deduce the type name from the argument.
// Pre-C++20 without helper
void OnThingCreated(ILongNamedThing* rawThing)
{
auto thing = wil::com_ptr<ILongNamedThing>(rawThing);
wil::com_ptr<ILongNamedThing> thing(rawThing);
RunAsync([thing = wil::com_ptr<ILongNamedThing>(rawThing)]
{ ... });
}
// Pre-C++20 with helper
void OnThingCreated(ILongNamedThing* rawThing)
{
auto thing = wil::make_com_ptr(rawThing);
RunAsync([thing = wil::make_com_ptr(rawThing)]
{ ... });
}
// C++20 and onward
void OnThingCreated(ILongNamedThing* rawThing)
{
auto thing = wil::com_ptr(rawThing);
wil::com_ptr thing(rawThing);
RunAsync([thing = wil::com_ptr(rawThing)]
{ ... });
}
wil::com_ptr_t supports exceptions and failfast that enables concise use like this.
someObj.query<ISomeInterface>()->CallMethod();
ComPtr only supports error codes. If you are using exceptions or desire failfast behavior, the choice is clear. If you are converting to exceptions use wil::com_ptr. If you are HRESULT-based and the code already uses WRL ComPtr, you can continue to use it. If you are HRESULT-based and the code is new prefer using wil::com_ptr_nothrow in new code.
-
T* com_raw_ptr(x)
Extracts the underlying raw pointer.x
can be a raw pointer, acom_ptr_t<T>
, aWRL::ComPtr<T>
, or a C++/CXT^
hat pointer. (For C++/CX hat pointers, the raw pointer is alwaysIInspectable*
.)
Direct | Try |
---|---|
com_ptr<U> com_query<U>(x) com_ptr_failfast<U> com_query_failfast<U>(x) (no nothrow version) |
com_ptr<U> try_com_query<U>(x) com_ptr_failfast<U> try_com_query_failfast<U>(x) com_ptr_nothrow<U> try_com_query_nothrow<U>(x)
|
com_ptr<U> com_copy<U>(x) com_ptr_failfast<U> com_copy_failfast<U>(x) (no nothrow version) |
com_ptr<U> try_com_copy<U>(x) com_ptr_failfast<U> try_com_copy_failfast<U>(x) com_ptr_nothrow<U> try_com_copy_nothrow<U>(x)
|
void com_query_to(x, U** p) void com_query_to_failfast(x, U** p) HRESULT com_query_to_nothrow(x, U** p)
|
bool try_com_query_to(x, U** p) bool try_com_query_to_failfast(x, U** p) bool try_com_query_to_nothrow(x, U** p)
|
void com_copy_to(x, U** p) void com_copy_to_failfast(x, U** p) HRESULT com_copy_to_nothrow(x, U** p)
|
bool try_com_copy_to(x, U** p) (no fastfail version) (no nothrow version) |
void com_query_to(x, REFIID riid, void** ppv) void com_query_to_failfast(x, REFIID riid, void** ppv) HRESULT com_query_to_nothrow(x, REFIID riid, void** ppv)
|
bool try_com_query_to(x, REFIID riid, void** ppv) bool try_com_query_to_failfast(x, REFIID riid, void** ppv) bool try_com_query_to_nothrow(x, REFIID riid, void** ppv)
|
void com_copy_to(x, REFIID riid, void** ppv) void com_copy_to_failfast(x, REFIID riid, void** ppv) HRESULT com_copy_to_nothrow(x, REFIID riid, void** ppv)
|
bool try_com_copy_to(x, REFIID riid, void** ppv) (no fastfail version) (no nothrow version) |
auto com_multi_query(IUnknown* obj) |
auto try_com_multi_query(IUnknown* obj) |
These are free function versions of the query and copy methods. They
take anything that com_raw_ptr
accepts as the first parameter and
behave the same as the corresponding query and copy methods. These
functions are handy if you have a raw pointer in hand and want to
perform a COM operation on it.
-
Object^ wil::cx_object_from_abi(x)
Takes anything thatcom_raw_ptr
accepts, converts it toIInspectable*
, and then toObject^
. -
U^ wil::cx_safe_cast<U>(x)
Takes anything thatcom_raw_ptr
accepts, converts it toIInspectable*
, and then toObject^
, and thensafe_cast
s it toU^
. -
U^ wil::cx_dynamic_cast<U>(x)
Takes anything thatcom_raw_ptr
accepts, converts it toIInspectable*
, and then toObject^
, and thendynamic_cast
s it toU^
.
The wil::com_agile_ref
family of classes is an alias for
com_ptr<IAgileReference>
, with differences noted
below.
Class | Equivalent to | Description |
---|---|---|
com_agile_ref |
com_ptr<IAgileReference> |
IAgileReference wrapper which throws on error. |
com_agile_ref_failfast |
com_ptr_failfast<IAgileReference> |
IAgileReference wrapper which fails fast on error. |
com_agile_ref_nothrow |
com_ptr_nothrow<IAgileReference> |
IAgileReference wrapper which returns error codes. |
The com_agile_ref
wrapper class differs from a regular com_ptr
in
that the query
and copy
methods operate on the target of the agile
reference, rather than on the agile reference itself. In other words,
they use IAgileReference::Resolve
rather than
IUnknown::QueryInterface
. Every query
and copy
operation results
in a resolve operation; there is no short-circuit like there is for
regular com_ptr
s.
Additional free function helpers to assist with using com_agile_ref
objects:
Query family | Copy family | Description |
---|---|---|
com_agile_ref com_agile_query(anything) |
com_agile_ref com_agile_copy(anything) |
Returns a throwing com_agile_ref . |
com_agile_ref_failfast com_agile_query_failfast(anything) |
com_agile_ref_failfast com_agile_copy_failfast(anything) |
Returns a fail-fast com_agile_ref_failfast . |
HRESULT com_agile_query_nothrow(anything, IAgileReference** result) |
HRESULT com_agile_copy_nothrow(anything, IAgileReference** result) |
Returns an error-code based com_agile_ref_nothrow . |
anything must not be a null pointer or empty wrapper. |
anything can be a null pointer or empty wrapper. |
|
The anything
parameter can be anything that com_raw_ptr
accepts, and
the agile reference wraps the COM interface that anything
represents.
In the case of the copy functions, it is legal to pass a null pointer or
empty wrapper, in which case the function succeeds with an empty agile
reference (for com_agile_copy
and com_agile_copy_failfast
) or
succeeds with a null pointer (for com_agile_copy_nothrow
).
All of the above functions accept an optional AgileReferenceOptions
final parameter. If omitted, it defaults to AGILEREFERENCE_DEFAULT
.
One final free function:
-
bool is_agile(anything)
Determines whether the object is agile, by querying it forIAgileObject
. Theanything
parameter can be anything thatcom_raw_ptr
accepts, and it may not be a null pointer or empty wrapper.
The wil::com_weak_ref
family of classes is an alias for
com_ptr<IWeakReference>
, with differences noted
below.
Class | Equivalent to | Description |
---|---|---|
com_weak_ref |
com_ptr<IWeakReference> |
IWeakReference wrapper which throws on error. |
com_weak_ref_failfast |
com_ptr_failfast<IWeakReference> |
IWeakReference wrapper which fails fast on error. |
com_weak_ref_nothrow |
com_ptr_nothrow<IWeakReference> |
IWeakReference wrapper which returns error codes. |
The com_weak_ref
wrapper class differs from a regular com_ptr
in
that the query
and copy
methods operate on the target of the weak
reference, rather than on the weak reference itself. In other words,
they use IWeakReference::Resolve
rather than
IUnknown::QueryInterface
. Every query
and copy
operation results
in a resolve operation; there is no short-circuit like there is for
regular com_ptr
s.
If the target of the weak reference has been destroyed, then the
operation fails with E_NOT_SET
. Note that this is different from how
COM itself reports failure, which is RPC_E_DISCONNECTED
.
The object from which you obtain a weak reference must implement
IInspectable
.
These free function helpers assist with creating com_weak_ref
objects:
Query family | Copy family | Description |
---|---|---|
com_weak_ref com_weak_query(anything) |
com_weak_ref com_weak_copy(anything) |
Returns a throwing com_weak_ref . |
com_weak_ref_failfast com_weak_query_failfast(anything) |
com_weak_ref_failfast com_weak_copy_failfast(anything) |
Returns a fail-fast com_weak_ref_failfast . |
HRESULT com_weak_query_nothrow(anything, IAgileReference** result) |
HRESULT com_weak_copy_nothrow(anything, IAgileReference** result) |
Returns an error-code based com_weak_ref_nothrow . |
anything must not be a null pointer or empty wrapper. |
anything can be a null pointer or empty wrapper. |
|
The anything
parameter can be anything that com_raw_ptr
accepts.
In the case of the copy functions, it is legal to pass a null pointer or
empty wrapper, in which case the function succeeds with an empty weak
reference (for com_weak_copy
and com_weak_copy_failfast
) or succeeds
with a null pointer (for com_weak_copy_nothrow
).
The object creation helpers follow these patterns:
com_ptr<Interface> CoCreateInstance<Class, Interface>(CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
com_ptr<Interface> CoCreateInstance<Interface>(REFCLSID rclsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
com_ptr<Interface> CoCreateInstance<Interface>(REFCLSID rclsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
com_ptr<Interface> CoGetClassObject<Class, Interface>(CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
com_ptr<Interface> CoGetClassObject<Interface>(REFCLSID rclsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
auto TryCoCreateInstanceEx<Interface>(REFCLSID rclsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
Notes:
- You can specify a
Class
(which must support__uuidof
) as a template parameter or aREFCLSID
as a function parameter. - The
Interface
template parameter is optional and defaults toIUnknown
. - The
dwClsContext
function parameter is optional and defaults toCLSCTX_INPROC_SERVER
.
The following table demonstrates all the error policies.
Error policy | Functions |
---|---|
err_exception_policy Throws on failure. |
com_ptr<Interface> CoCreateInstance(...) com_ptr<Interface> CoGetClassObject(...) |
err_failfast_policy Fails fast on failure. |
com_ptr_failfast<Interface> CoCreateInstanceFailFast(...) com_ptr_failfast<Interface> CoGetClassObjectFailFast(...) |
err_returncode_policy Returns empty on failure. |
com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(...) com_ptr_nothrow<Interface> CoGetClassObjectNoThrow(...) |
Parameters are typed as follows:
-
ISequentialStream* stream
, must not be null. -
void* dest
, must besize
bytes. -
const void* source
, must besize
bytes. -
unsigned long size
, size ofdest
orsource
buffer in bytes. -
long long distance
, relative position in the stream. -
unsigned long long position
, absolute position in the stream (relative to stream start). -
unsigned long origin
, ASTREAM_SEEK
value which describes how thedistance
should be interpreted. -
ISequentialStream* sourceStream
andISequentialStream* targetStream
for the stream copying methods. -
unsigned long long amount
, number of bytes to copy.
All of the helper functions wrap a single call to the underlying COM stream. If a read, write, or copy operation produces a partial result, they do not loop internally to read, write, or copy the remaining data.
The _nothrow
version is the same as the throwing version, except that
it returns HRESULT
and the return value (if any) of the throwing
version is an explicit final output parameter of the same type as the
return value of the throwing
version.
Throwing version | Nonthrowing version | Note |
---|---|---|
void something(...) |
HRESULT something(...) |
|
ReturnType something(...) |
HRESULT something_nothrow(..., ReturnType* result) |
|
HRESULT something_nothrow(..., ReturnType* result = nullptr) |
Final parameter is optional. |
Function | Description |
---|---|
unsigned long |
stream_read_partial(stream, dest, size) |
HRESULT |
stream_read_partial_nothrow(stream, dest, size, unsigned long* result) |
void |
stream_read(stream, dest, size) |
HRESULT |
stream_read_nothrow(stream, dest, size) |
void |
stream_read(stream, T* dest) |
HRESULT |
stream_read_nothrow(stream, T* dest) |
void |
stream_write(stream, source, size) |
HRESULT |
stream_write_nothrow(stream, source, size) |
void |
stream_write(stream, const T* source) |
HRESULT |
stream_write_nothrow(stream, const T* source) |
unsigned long long |
stream_size(stream) |
HRESULT |
stream_size_nothrow(stream, unsigned long long* result) |
unsigned long long |
stream_seek(stream, distance, origin) |
HRESULT |
stream_seek_nothrow(stream, distance, origin, unsigned long long* result = nullptr) |
unsigned long long |
stream_set_position(stream, position) |
HRESULT |
stream_set_position_nothrow(stream, position, unsigned long long* result = nullptr) |
unsigned long long |
stream_seek_from_current_position(stream, distance) |
HRESULT |
stream_seek_from_current_position_nothrow(stream, distance, unsigned long long* result = nullptr) |
unsigned long long |
stream_get_position(stream) |
HRESULT |
stream_get_position_nothrow(stream, unsigned long long* result) |
void |
stream_reset(stream) |
HRESULT |
stream_reset_nothrow(stream) |
unsigned long long |
stream_copy_bytes(sourceStream, targetStream, amount) |
HRESULT |
stream_copy_bytes_nothrow(sourceStream, targetStream, amount, unsigned long long* result = nullptr) |
unsigned long long |
stream_copy_all(sourceStream, targetStream) |
HRESULT |
stream_copy_all_nothrow(sourceStream, targetStream, unsigned long long* result = nullptr) |
void |
stream_copy_exact(sourceStream, targetStream, amount) |
HRESULT |
stream_copy_exact_nothrow(sourceStream, targetStream, amount) |
Note 1: Fails with HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
if the
number of bytes transferred is not equal to the number of bytes
requested. Note that this failure code is thrown even for incomplete
writes, which is misleading because there is nothing wrong with the data
being written; the problem is that the stream is full.
Return value | Function |
---|---|
void |
stream_write_string(ISequentialStream* stream, const wchar_t sourceString, size_t characterCount) |
HRESULT |
stream_write_string_nothrow(ISequentialStream* stream, const wchar_t* sourceString, size_t characterCount) |
unique_cotaskmem_string |
stream_read_string(ISequentialStream* stream, options = returns_empty) |
HRESULT |
stream_read_string_nothrow(ISequentialStream* stream, PWSTR* result, options = returns_empty) |
The stream_write_string
functions write a counted string to the
stream. The format is a 16-bit character count, followed by that many
16-bit values. No null terminator is appended. This is the same format
used by the IStream_WriteStr
function.
The characterCount
is typically wcslen(sourceString)
, but can be
less if you need to write only a portion of the string.
If the characterCount
exceeds 65535, a fail-fast exception is raised,
even for the _nothrow
version.
The stream_read_string
functions read a string written by
stream_write_string
. The returned string must be freed with
CoTaskFreeMemory
.
The optional options
parameter controls how zero-length strings are
returned.
Option | Description |
---|---|
wil::empty_string_options::returns_empty (default) |
zero-length strings are returned as an allocated empty string. |
wil::empty_string_options::returns_null |
zero-length strings are returned as nullptr . |
The stream_position_saver
class remembers the position of the optional
stream at construction and restores the stream position when it is
destructed.
The stream_position_saver
is said to be empty if it does not have a
captured position. An empty stream_position_saver
does nothing when
destructed.
Example usage:
// On error, restore the stream position to where it was when we started.
// That way, somebody else can try to read the stream.
auto saver = wil::stream_position_saver(stream);
auto header = wil::stream_read<MY_HEADER>(stream);
if (header.signature != MY_HEADER_SIGNATURE)
{
// Reject the stream. The "saver" will restore the position.
THROW_HR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
}
// We are happy with the stream.
// Cancel the restoration of the stream position.
saver.dismiss();
The stream_position_saver
can be moved but not copied. Moving a
stream_position_saver
transfers the obligation to restore the stream
position to the moved-to object. The moved-from object becomes empty.
Constructor
-
stream_position_saver(IStream* stream)
Captures the position of the stream for future restoration. Throws if the position of the stream cannot be obtained. Thestream
can benullptr
, in which case no position is saved.
Destructor
-
~stream_position_saver
Restores the saved position of the stream, if nonempty. If the attempt to restore the stream position fails, an error is logged but execution continues.
Methods
-
void update()
Update the saved position to match the current position of the stream. Throws if the position cannot be obtained. Crashes if thestream_position_saver
is empty. -
unsigned long long position()
Returns the captured position. Returns a garbage value if thestream_position_saver
is empty. -
void reset()
Restores the saved position of the stream. Throws if the position cannot be restored. Thestream_position_saver
continues to remember this position and will restore the position again at destruction. -
void reset(IStream* newStream)
Restores the saved position of the stream and captures the position ofnewStream
for future restoration. Throws if the position cannot be restored or if the position of the new stream cannot be obtained. (There is a bug in the current implementation: If the position of the new stream cannot be obtained, we will incorrectly set its position to the position captured from the original stream.) ThenewStream
may not benullptr
. -
void dismiss()
Cancels the restoration of the saved stream position and makes thestream_position_saver
empty.
wil::unique_set_site_null_call
is an RAII type that calls
IObjectWithSite::SetSite(nullptr)
on destruction. it follows the
unique_com_call
pattern; see
unique_com_call
for further details.
This is usually not constructed explicitly, but is rather returned by
com_set_site
so that you can set a site, perform an operation, and
then clear the site.
Explicit usage:
// For demonstration purposes only. This pattern is encapsulated in the com_set_site method,
// which you should use instead of writing out these three lines manually.
auto objectWithSite = someObject.query<IObjectWithSite>();
objectWithSite->SetSite(someSite);
auto cleanup = wil::unique_set_site_null_call(objectWithSite.get());
someObject->Execute();
// when cleanup goes out of scope, it will set the site back to nullptr.
The wil::com_set_site
helper method sets an optional site on an
optional object which optionally implements IObjectWithSite
. It
returns a unique_set_site_null_call
which sets the site back to null
upon destruction. This is the typical way of using
unique_set_site_null_call
.
Gotcha: If you forget to save the result of com_set_site
, then the
site is reset immediately, and it looks like your com_set_site
didn't
do
anything.
unique_set_site_null_call com_set_site(_In_opt_ IUnknown* obj, _In_opt_ IUnknown* site);
If obj
is null, or if it does not implement IObjectWithSite
, then
nothing happens, and the resulting unique_set_site_null_call
is empty.
If obj
implements IObjectWithSite
, then its site is set to site
,
and the resulting unique_set_site_null_call
sets obj
's site back to
null upon destruction.
Example usage:
auto obj = ...;
auto cleanup = wil::com_set_site(obj.get(), site.get());
someObject->Execute();
// when cleanup goes out of scope, it will set the site back to nullptr.
If you want to set the site and are not interested in restoring it afterward, then use
auto obj = ...;
wil::com_set_site(obj.get(), site.get()).release();
The for_each_site
function invokes its callback once for each site in
the site chain. All errors are ignored and cause the site chain walk to
terminate. it is typically used for debugging.
template<typename TLambda>
void for_each_site(_In_opt_ IUnknown* obj, TLambda&& callback);
Example usage:
void OutputDebugSiteChainWatchWindowText(IUnknown* site)
{
OutputDebugStringW(L"Copy and paste these entries into the Visual Studio Watch Window\n");
wil::for_each_site(site, [](IUnknown* site)
{
wchar_t msg[64];
StringCchPrintfW(msg, ARRAYSIZE(msg), L"((IUnknown*)0x%p)->__vfptr[0]\n", site);
OutputDebugStringW(msg);
});
}
COM apartment variables are stored in the current COM apartment, every apartment has unique storage. When the apartment runs down the variables are released. This avoids the problems (undefined behavior, crashes) of using COM objects in global variables and enables caching of information relative to the apartment. To get the same effect as a global variable, that is one instance per DLL, make sure use is always from the MTA.
template<typename T> struct apartment_variable
-
T
- The thing to be stored, a COM object (e.g.wil::com_ptr
) or an aggregate that contains COM objects.
-
~apartment_variable()
Releases T and the COM objects it represents. When necessary, starts and async rundown of the variables stored in other apartments.
-
T& get_existing()
Get current value or throw if no value has been set. -
T& get_or_create()
Get current value or default-construct one on demand. -
template<typename F> T& get_or_create(F&& f)
Get current value or custom-construct one on demand by invoking f to get the value. -
T* get_if()
get pointer to current value ornullptr
if no value has been set -
template<typename V> void set(V&& value)
Replace or create the current value, fail fasts if the value is not already stored. Use this when the stored value is invalid and needs to be replaced with a new one. -
void clear()
Clear the value in the current apartment. -
winrt::IAsyncAction clear_all_apartments_async()
Asynchronously clear the value in all apartments.
Apartment variables hosted in a COM DLL need to integrate with the DllCanUnloadNow()
function
to include the ref counts contributed by C++ WinRT objects that wil::apartment_varaible
is based on.
This is automatic for DLLs that host C++ WinRT objects but WRL projects will need to be updated
to use winrt::get_module_lock()
in the DllCanUnloadNow()
implementation.
Example usage:
wil::apartment_variable<std::wstring> loggerName;
std::wstring GetNewLoggerName() { /* ... */ }
void LogThings() {
auto& name = loggerName.get_or_create(&GetNewLoggerName);
log_to_file(name);
if (log_file_getting_big())
{
name = GetNewLoggerName(); // roll over to new log file
loggerName.set(name);
}
}
void UseLogIfPresent() {
auto name = loggerName.get_if();
if (name) log_to_file(*name);
}
wil::apartment_variable<int> widgetCount;
void UpdateWidgetCounter()
{
++widgetCount.get_or_create();
}
There is a pattern that is followed by many COM interfaces known as IEnum. These interfaces expose a Next(ULONG cElemsToFetch, T* output, ULONG* cFetched)
API, for some type T.
Examples of these:
WIL provides helpers for using these interfaces as iterators:
wil::com_ptr<IEnumAssocHandlers> enumAssocHandlers;
wil::verify_hresult(SHAssocEnumHandlers(L".jpg", ASSOC_FILTER_RECOMMENDED, &enumAssocHandlers));
for (const wil::com_ptr<IAssocHandler>& assocHandler : wil::make_range(enumAssocHandlers.get()))
{
// do something with each assocHandler
}
These iterators can also be used with algorithms:
wil::com_ptr<IEnumAssocHandlers> enumAssocHandlers;
wil::verify_hresult(SHAssocEnumHandlers(L".jpg", ASSOC_FILTER_RECOMMENDED, &enumAssocHandlers));
auto iterator = wil::make_range(enumAssocHandlers.get());
const auto it = std::find_if(iterator.begin(), iterator.end(), [](const wil::com_ptr<IAssocHandler>& assocHandler)
{
// some criteria on the assocHandler
});
If the type that the enumerator enumerates is IUnknown-derived, you can simply invoke wil::make_range(foo)
.
Otherwise, you must pass a type template parameter for what "smart wrapper" type will be used to hold the enumerated value, e.g. a unique_any<T, ...>
for something like PCIDLSIT_CHILD
:
using unique_idlist = wil::unique_any<LPITEMIDLIST, decltype(&ILFree), ILFree>;
for (const auto& pidl : wil::make_range<unique_idlist>(enumIDList.get()))
{
// ...
}