-
Notifications
You must be signed in to change notification settings - Fork 236
RAII resource wrappers
WIL's resource wrappers provide a family of smart pointer patterns and resource wrappers that are designed to allow C++ consumers to consistently use RAII in all code. These patterns are in broad use across the Windows codebase, with well over 100 distinct RAII resource classes for various operating system resource types. The goals of these patterns are to provide:
- Uniform, consistent RAII (smart pointers) across all relevant Windows handle, resource, and locking types
- Mechanisms to easily create new, uniform RAII wrappers around commonly required cleanup patterns
- Simple usability wrappers around operating system concepts
The resource wrappers library is usable by any user-mode C++ code through relative inclusion of Resource.h.
#include <wil/resource.h>
Note that Resource.h defines wrappers only for types that have been
defined prior to the inclusion of Resource.h. It does not include any
system header files for you. In other words, if you want to use
Resource.h for a type defined in some header file, you must include that
other header file before including Resource.h. For example, if you
want to use unique_hinternet
, one must include WinINet.h before
including Resource.h.
#include <WinINet.h>
#include <wil/resource.h>
It is safe to include Resource.h multiple times. Each time will define wrappers for any new types defined after the previous inclusion of Resource.h.
In addition to the headers containing the types you wish to wrap, for creating new smart pointer types, WIL depends on the headers below:
Resource | Prerequisite |
---|---|
wil::weak_* |
<memory> |
wil::shared_* |
<memory> |
wil::make_*_nothrow |
<new> |
The interface to all of WIL's RAII / resource management classes is
modeled after
std::unique_ptr
.
You'll find a get()
method to access the resource, reset()
methods
to free or replace the resource and a release()
method to detach the
raw resource from the management class. See the docs for
std::unique_ptr
for complete reference.
An overview of WIL's resource patterns:
-
wil::unique_any
- Manages an opaque handle type (
HANDLE
,HKEY
,PSECURITY_DESCRIPTOR
, etc.)
- Manages an opaque handle type (
-
wil::unique_any_array_ptr
- Manages an allocated array of some other raw type that may
itself need managed (
DWORD
,HANDLE
,IFoo*
, etc.)
- Manages an allocated array of some other raw type that may
itself need managed (
-
wil::unique_struct
- Manages a raw structure that requires some form of cleanup
(
VARIANT
,PROCESS_INFORMATION
, etc.)
- Manages a raw structure that requires some form of cleanup
(
-
wistd::unique_ptr
- Manages a pointer to a typed allocation of any kind
-
wil::unique_com_token
- Manages a handle or token type retrieved from and freed with a
COM interface (
HENDPOINT
,DWORD
subscription tokens, etc.)
- Manages a handle or token type retrieved from and freed with a
COM interface (
-
wil::unique_com_call
- Manages a call made with a specific COM interface
(
IClosable::Close
, etc.)
- Manages a call made with a specific COM interface
(
-
wil::unique_call
- Manages a call made on a global method (
CoUninitialize
,CoRevertToSelf
, etc.)
- Manages a call made on a global method (
-
wil::scope_exit
- Manages a caller supplied lambda to be executed
WIL's unique_any is a traits-based smart pointer that can be specialized to hold any opaque resource type.
Unlike std::unique_ptr
(which is meant to hold a varying
specified by the caller), unique_any is meant to be used through a
dedicated typedef for each of the many unique handle and token types
that Windows exposes at the ABI level.
Here's a simple example typedef for an LSA_HANDLE contained within
WIL's
Resource.h
:
using unique_hlsa = wil::unique_any<LSA_HANDLE, decltype(&::LsaClose), ::LsaClose>;
Now, any caller which needs to deal with an LSA_HANDLE, can do so with a familiar RAII pattern modeled from the interface for unique_ptr:
unique_hlsa hlsa;
RETURN_IF_NTSTATUS_FAILED(LsaOpenPolicy(nullptr, &oa, POLICY_LOOKUP_NAMES, &hlsa));
RETURN_IF_FAILED(GetProfileSid(hlsa.get(), username, &sidString)))
Key differences from std::unique_ptr are:
- Exposes the '&' operator or put() member method for output parameter use (calling this frees any previously-held resource owned by the smart pointer)
- Supports non-pointer resource types (DWORD tokens, etc.)
- Supports non-null invalid values (INVALID_HANDLE_VALUE)
Basic usage is covered in the sample below. See the reference for
std::unique_ptr
for the complete rundown of the interface that unique_any is modeled
(nearly identically) after. See
RAII Resource wrapper details for further
information.
// Construct a new pointer with a resource
wil::unique_handle ptr(handle);
// Retrieve the resource
auto resource = ptr.get();
// Check validity of the resource
if (ptr)
{
// resource is assigned
}
// Same as previous
if (ptr.is_valid())
{
// resource is assigned
}
// Free the resource
ptr.reset();
// Free and replace the resource
ptr.reset(handle);
// Detach resource from the pointer without freeing
auto resource = ptr.release();
// Return the address of the internal resource for out parameter use
// Note: Also frees any currently-held resource
WindowsApiCall(&ptr);
// Same as previous
WindowsApiCall(ptr.put());
// Return the address of the internal resource for in-out parameter use
WindowsApiCall(ptr.addressof());
// Swap resources between smart pointers
ptr.swap(ptr2);
The unique_any
is exactly the same size as the wrapped type.
This allows an array of unique_any<T>
to be used as if it
were an array of T
.
Many resources use a common cleanup pattern, such as LocalFree
. The
following unique_any
template types can be used when the cleanup
function is a well-known cleanup function.
Note that the type T
must be trivially
destructible.
Cleanup function |
Generic type |
Scalar maker functions |
Array maker functions |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
(none currently defined) |
(none currently defined) |
|
|
|
|
|
|
|
|
- The
make_unique_xxx
methods forward the...
to theT
constructor. - The
make_unique_xxx<T[]>
methods allocate an array ofsize
objects of typeT
with the default constructor.
WIL's unique_any smart pointers contain the typical Windows resource
types that you would expect: file handles (unique_hfile
),
synchronization primitives (unique_event
, unique_semaphore
,
unique_mutex
), and many others. WIL's unique_any can be easily
specialized to include methods that operate on that type as well.
WIL covers a significant number of the unique resource types in the
Windows codebase. Search WIL's Resource.h
for the type you need; if
you don't find it, and it isn't already covered by the unique_xxx_ptr
pattern above, then consider adding a smart pointer for it, documenting
it in this table and making a pull
request.
Type | Wrapped object | Cleanup | Notes |
---|---|---|---|
unique_any_psid |
PSID |
LocalFree |
|
unique_bcrypt_algorithm |
BCRYPT_ALG_HANDLE |
BCryptCloseAlgorithmProvider |
|
unique_bcrypt_hash |
BCRYPT_HASH_HANDLE |
BCryptDestroyHash |
|
unique_bcrypt_key |
BCRYPT_KEY_HANDLE |
BCryptDestroyKey |
|
unique_bcrypt_secret |
BCRYPT_SECRET_HANDLE |
BCryptDestroySecret |
|
unique_bstr |
BSTR |
SysFreeString |
|
unique_cert_chain_context |
PCCERT_CHAIN_CONTEXT |
CertFreeCertificateChain |
|
unique_cert_context |
PCCERT_CONTEXT |
CertFreeCertificateContext |
cert_context_t methods available. |
unique_com_class_object_cookie |
DWORD |
CoRevokeClassObject |
|
unique_cotaskmem |
void* |
CoTaskMemFree |
|
unique_cotaskmem_ansistring |
PSTR |
CoTaskMemFree |
|
unique_cotaskmem_psid |
PSID |
CoTaskMemFree |
|
unique_cotaskmem_string |
PWSTR |
CoTaskMemFree |
|
unique_cotaskmem_string_secure |
PWSTR |
SecureZeroMemory+CoTaskMemFree |
|
unique_midl_ansistring |
PSTR |
MIDL_user_free |
|
unique_midl_string |
PWSTR |
MIDL_user_free |
|
unique_event |
HANDLE |
CloseHandle |
event_t methods available |
unique_event_failfast |
HANDLE |
CloseHandle |
event_t methods available |
unique_event_nothrow |
HANDLE |
CloseHandle |
event_t methods available |
unique_event_watcher |
event_watcher_state* |
CloseHandle |
|
unique_event_watcher_failfast |
event_watcher_state* |
CloseHandle |
|
unique_event_watcher_nothrow |
event_watcher_state* |
CloseHandle |
|
unique_file |
FILE* |
fclose |
|
unique_haccel |
HACCEL |
DestroyAccelerator |
|
unique_handle |
HANDLE |
CloseHandle |
|
unique_hbitmap |
HBITMAP |
DeleteObject |
|
unique_hbrush |
HBRUSH |
DeleteObject |
|
unique_hcertstore |
HCERTSTORE |
CertCloseStore |
|
unique_hcmnotification |
HCMNOTIFICATION |
CM_Unregister_Notification |
|
unique_hcrypthash |
HCRYPTHASH |
CryptDestroyHash |
|
unique_hcryptkey |
HCRYPTKEY |
CryptDestroyKey |
|
unique_hcryptprov |
HCRYPTPROV |
CryptReleaseContext |
|
unique_hcursor |
HCURSOR |
DestroyCursor |
|
unique_hdc |
HDC |
DeleteDC |
See below |
unique_hdc_paint |
HDC |
EndPaint |
See below |
unique_hdc_window |
HDC |
ReleaseDC |
See below |
unique_hdesk |
HDESK |
CloseDesktop |
|
unique_hfile |
HANDLE |
CloseHandle |
|
unique_hfind |
HANDLE |
FindClose |
|
unique_hfind_change |
HANDLE |
FindCloseChangeNotification |
|
unique_hfont |
HFONT |
DeleteObject |
|
unique_hgdiobj |
HGDIOBJ |
DeleteObject |
|
unique_hglobal |
HGLOBAL |
GlobalFree |
|
unique_hglobal_ansistring |
PSTR |
GlobalFree |
|
unique_hglobal_locked |
HGLOBAL |
GlobalUnlock |
Calls GlobalLock on construction. |
unique_hglobal_string |
PWSTR |
GlobalFree |
|
unique_hheap |
HANDLE |
HeapDestroy |
|
unique_hhook |
HHOOK |
UnhookWindowsHookEx |
|
unique_hicon |
HICON |
DestroyIcon |
|
unique_himagelist |
HIMAGELIST |
ImageList_Destroy |
|
unique_hinternet |
HINTERNET |
InternetCloseHandle |
|
unique_hkey |
HKEY |
RegCloseKey |
|
unique_hlocal |
HLOCAL |
LocalFree |
|
unique_hlocal_ansistring |
PSTR |
LocalFree |
|
unique_hlocal_security_descriptor |
PSECURITY_DESCRIPTOR |
LocalFree |
|
unique_hlocal_string |
PWSTR |
LocalFree |
|
unique_hlocal_string_secure |
PWSTR |
SecureZeroMemory+LocalFree |
|
unique_hlsa |
LSA_HANDLE |
LsaClose |
|
unique_hlsalookup |
LSA_HANDLE |
LsaLookupClose |
|
unique_hmenu |
HMENU |
DestroyMenu |
|
unique_hmodule |
HMODULE |
FreeLibrary |
|
unique_hpalette |
HPALETTE |
DeleteObject |
|
unique_hpen |
HPEN |
DeleteObject |
|
unique_hpowernotify |
HPOWERNOTIFY |
UnregisterPowerSettingNotification |
|
unique_hrgn |
HRGN |
DeleteObject |
|
unique_hstring |
HSTRING |
WindowsDeleteString |
|
unique_hstring_buffer |
HSTRING_BUFFER |
WindowsDeleteStringBuffer |
|
unique_htheme |
HTHEME |
CloseThemeData |
|
unique_hwineventhook |
HWINEVENTHOOK |
UnhookWinEvent |
|
unique_hwinsta |
HWINSTA |
CloseWindowStation |
|
unique_hwnd |
HWND |
DestroyWindow |
|
unique_mib_iftable |
PMIB_IF_TABLE2 |
FreeMibTable |
|
unique_mta_usage_cookie |
CO_MTA_USAGE_COOKIE |
CoDecrementMTAUsage |
|
unique_mutex |
HANDLE |
CloseHandle |
mutex_t methods available |
unique_mutex_failfast |
HANDLE |
CloseHandle |
mutex_t methods available |
unique_mutex_nothrow |
HANDLE |
CloseHandle |
mutex_t methods available |
unique_ncrypt_key |
NCRYPT_KEY_HANDLE |
NCryptFreeObject |
|
unique_ncrypt_prov |
NCRYPT_PROV_HANDLE |
NCryptFreeObject |
|
unique_ncrypt_secret |
NCRYPT_SECRET_HANDLE |
NCryptFreeObject |
|
unique_package_info_reference |
PACKAGE_INFO_REFERNCE |
ClosePackageInfo |
|
unique_pipe |
FILE* |
_pclose |
|
unique_private_security_descriptor |
PSECURITY_DESCRIPTOR |
DestroyPrivateObjectSecurity |
|
unique_process_handle |
HANDLE |
CloseHandle |
|
unique_process_heap_string |
PWSTR |
HeapFree+GetProcessHeap |
|
unique_rpc_binding |
RPC_BINDING_HANDLE |
RpcBindingFree |
|
unique_rpc_binding_vector |
RPC_BINDING_VECTOR* |
RpcBindingVectorFree |
|
unique_rpc_pickle |
handle_t |
MesHandleFree |
|
unique_rpc_wstr |
RPC_WSTR |
RpcStringFreeW |
|
unique_scardctx |
SCARDCONTEXT |
SCardReleaseContext |
|
unique_schandle |
SC_HANDLE |
CloseServiceHandle |
|
unique_select_object |
HGDIOBJ |
SelectObject |
See below |
unique_sid |
PSID |
FreeSid |
|
unique_semaphore |
HANDLE |
CloseHandle |
semaphore_t methods available |
unique_semaphore_failfast |
HANDLE |
CloseHandle |
semaphore_t methods available |
unique_semaphore_nothrow |
HANDLE |
CloseHandle |
semaphore_t methods available |
unique_socket |
SOCKET |
closesocket |
|
unique_threadpool_io |
PTP_IO |
CloseThreadpoolIo |
|
unique_threadpool_io_nocancel |
PTP_IO |
CloseThreadpoolIo |
|
unique_threadpool_io_nowait |
PTP_IO |
CloseThreadpoolIo |
|
unique_threadpool_timer |
PTP_TIMER |
CloseThreadpoolTimer |
See below |
unique_threadpool_timer_nocancel |
PTP_TIMER |
CloseThreadpoolTimer |
See below |
unique_threadpool_timer_nowait |
PTP_TIMER |
CloseThreadpoolTimer |
See below |
unique_threadpool_wait |
PTP_WAIT |
CloseThreadpoolWait |
See below |
unique_threadpool_wait_nocancel |
PTP_WAIT |
CloseThreadpoolWait |
See below |
unique_threadpool_wait_nowait |
PTP_WAIT |
CloseThreadpoolWait |
See below |
unique_threadpool_work |
PTP_WORK |
CloseThreadpoolWork |
See below |
unique_threadpool_work_nocancel |
PTP_WORK |
CloseThreadpoolWork |
See below |
unique_threadpool_work_nowait |
PTP_WORK |
CloseThreadpoolWork |
See below |
unique_tls |
DWORD |
TlsFree |
|
unique_tool_help_snapshot |
HANDLE |
CloseHandle |
|
unique_wdf_any
|
T |
WdfObjectDelete |
|
unique_wdf_collection |
WDFCOLLECTION |
WdfObjectDelete |
|
unique_wdf_common_buffer |
WDFCOMMONBUFFER |
WdfObjectDelete |
|
unique_wdf_dma_enabler |
WDFDMAENABLER |
WdfObjectDelete |
|
unique_wdf_dma_transaction |
WDFDMATRANSACTION |
WdfObjectDelete |
|
unique_wdf_key |
WDFKEY |
WdfObjectDelete |
|
unique_wdf_memory |
WDFMEMORY |
WdfObjectDelete |
|
unique_wdf_object |
WDFOBJECT |
WdfObjectDelete |
|
unique_wdf_spin_lock |
WDFSPINLOCK |
WdfObjectDelete |
See below |
unique_wdf_string |
WDFSTRING |
WdfObjectDelete |
|
unique_wdf_timer |
WDFTIMER |
WdfObjectDelete |
|
unique_wdf_wait_lock |
WDFWAITLOCK |
WdfObjectDelete |
See below |
unique_wdf_work_item |
WDFWORKITEM |
WdfObjectDelete |
|
unique_wdf_object_reference |
T |
WdfObjectDereferenceWithTag |
See below |
unique_wer_report |
HREPORT |
WerReportCloseHandle |
|
unique_winhttp_hinternet |
HINTERNET |
WinHttpCloseHandle |
|
unique_wlan_handle |
HANDLE |
CloseWlanHandle |
|
unique_allocated_irp |
PIRP |
IoFreeIrp |
|
unique_io_workitem |
PIO_WORKITEM |
IoFreeWorkItem |
|
unique_kernel_handle |
HANDLE |
ZwClose |
|
Notes on particular types above:
-
unique_handle
is for kernel handles wherenullptr
is the null state, whileunique_hfile
is for file handles whereINVALID_HANDLE_VALUE
is the null state. -
For threadpool handles, the plain, unqualified name cancels on destruction and waits for any outstanding callback to complete, the
_nocancel
suffix waits on destruction, and the_nowait
suffix neither cancels nor waits. -
A more idiomatic name for
unique_any_psid
might beunique_hlocal_sid
, as it frees the SID withLocalFree
and there is nothing about SIDs that requires them to be allocated withLocalAlloc
. -
unique_event
has a lightweight variantwil::slim_event
. See below for details. -
unique_hdc
frees the DC withDeleteDC
,unique_hdc_window
frees the DC withReleaseDC
(and the window handle is available from thehwnd
member), andunique_hdc_paint
frees the DC withEndPaint
(and the window andPAINTSTRUCT
are available in thehwnd
andps
members). -
unique_wdf_spin_lock
andunique_wdf_wait_lock
provide lock methods that return RAII lock guard objects that automatically release the lock when they go out of scope. See WDF Resource Classes for more details.
You can use unique_any to define new publicly available RAII types or to cover RAII needs for your own private types.
Following is a simple example of how a new unique_any type can be defined:
typedef unique_any<SC_HANDLE, decltype(&::CloseServiceHandle), ::CloseServiceHandle> unique_schandle;
The required elements are the handle type (SC_HANDLE
), the type of the
function used to free that handle type
(decltype(&::CloseServiceHandle)
) and the actual function pointer to
that function (::CloseServiceHandle
). Though it is rarely needed, you
can also optionally specify the value of the 'invalid' handle type if
it's not zero (or null); see unique_socket
in Resource.h for an
example.
Advanced usage of unique_any
allows you to customize access to the
wrapped pointer, the type used to store the wrapped pointer, and the
invalid value for the wrapped pointer.
Here's an advanced usage for a TLS index, which is stored in a DWORD
rather than a pointer, and its invalid value is TLS_OUT_OF_INDEXES
rather than
zero.
typedef unique_any<DWORD, decltype(&::TlsFree), ::TlsFree, details::pointer_access_all, DWORD, TLS_OUT_OF_INDEXES, DWORD> unique_tls;
The template parameters are
-
DWORD
: The underlying type of the wrapped value. -
decltype(&::TlsFree), ::TlsFree
: The function to call to free the wrapped value. -
details::pointer_access_all
: Controls access to the wrapped value. The default ispointer_access_all
, but this is a non-optional parameter if you need to customize any subsequent template parameters. See the table of valid access levels below. -
DWORD
: The physical storage for the wrapped value. It defaults to (and is almost always the same as) the first template parameter. -
TLS_OUT_OF_INDEX
: The invalid value. Defaults to zero /nullptr
. -
DWORD
: The type of the invalid value. This is almost always the same as the first template parameter. Defaults tonullptr_t
; you must customize if your invalid value is not a pointer.
There are special helpers if the wrapped value is a
HANDLE
.
Helper | Default value | Invalid value(s) | Example |
---|---|---|---|
unique_any_handle_null |
nullptr |
nullptr or INVALID_HANDLE_VALUE
|
typedef unique_any_handle_null<decltype(&::CloseHandle), ::CloseHandle> unique_handle; |
unique_any_handle_invalid |
INVALID_HANDLE_VALUE |
nullptr or INVALID_HANDLE_VALUE
|
typedef unique_any_handle_invalid<decltype(&::FindClose), ::FindClose> unique_hfile; |
unique_any_handle_null_only |
nullptr |
nullptr only |
typedef unique_any_handle_null<decltype(&::CloseEventLog), ::CloseEventLog> unique_event_log; |
Level | get() |
release() |
operator& |
addressof() |
---|---|---|---|---|
details::pointer_access_all |
Allowed | Allowed | Allowed | Allowed |
details::pointer_access_noaddress |
Allowed | Allowed | Blocked | Blocked |
details::pointer_access_none |
Blocked | Blocked | Blocked | Blocked |
Access levels
See RAII Resource wrapper details for further information and even more advanced usage.
WIL provides wil::shared_any
that allows you to create a reference
counted version of any unique_any
type.
For example, WIL's Resource.h
defines a shared pointer for
unique_handle:
using shared_handle = wil::shared_any<unique_handle>;
The basic usage contract for a shared_handle is the union of the
std::shared_ptr
contract and WIL's
unique_any
contract.
The key difference to point out is that since shared_ptr
is an
exception-based contract, you can only use shared_any
from
exception-based code. This leads to the ability for the following
operations to throw a memory exhaustion exception:
// Any of the following operations may throw
// Note: resources will still be released even if there is an exception
wil::shared_handle ptr(handle);
ptr.reset(handle);
WindowsApiCall(&ptr);
WindowsApiCall(ptr.put());
WindowsApiCall(ptr.addressof());
Similarly, WIL also defines weak_any
which behaves as the
std::weak_ptr
equivalent for shared_any
, for example:
using weak_handle = wil::weak_any<shared_handle>;
Occasionally ABI contracts can return an allocated array and count as part of their contract, and require the elements in the array to be independently freed before the array itself can be freed.
WIL's unique_any_array_ptr can be used to handle these situations by
representing count and a pointer to an allocated array of items. Both
the array and the items are freed with custom deleters, allowing the
type to represent things like a cotaskmem
-allocated array of handles,
where freeing the array involves closing each handle before freeing the
array.
Use of unique_any_array_ptr
requires specifying the type held in the
array and the deleters for the array and array
elements:
template <typename ValueType, typename ArrayDeleter, typename ElementDeleter = empty_deleter>
class unique_any_array_ptr
WIL defines unique_array_ptr
to simplify use by utilizing type traits
to derive the array element type and the array element deleter from any
WIL smart pointer. This is then most commonly used through a type
definition (such as unique_cotaskmem_array_ptr
) that defines the
allocator used for the array.
template <typename T, typename ArrayDeleter>
using unique_array_ptr
template <typename T>
using unique_cotaskmem_array_ptr = unique_array_ptr<T, cotaskmem_deleter>;
As a real-world example:
wil::unique_cotaskmem_array_ptr<wil::unique_hstring> stringArray;
THROW_IF_FAILED(propVal->GetStringArray(stringArray.size_address<UINT32>(), &stringArray));
Use of wil::unique_hstring
is for type traits; it specifies that the
cotaskmem array is a raw allocated array of HSTRING
s and provides the
deleter required to free the HSTRING
s when the array is freed. Notice
that both the array pointer and size are set on the same call. Since
this function doesn't use size_t for it's size, a templated
size_address routine is used to specify the appropriate type for the
routine.
The accepted smart pointer types supported by unique_array_ptr are:
- POD types (simple data types)
-
unique_any
-
unique_struct
-
com_ptr
For POD types, the array is freed with with CoTaskMemFree
.
Individual elements are not destructed. If your objects require
special cleanup, use a unique_any
or unique_struct
-derived type.
(See below.)
wil::unique_cotaskmem_array_ptr<INT32> int32Array;
RETURN_IF_FAILED(propertyValue->GetInt32Array(int32Array.size_address<UINT32>(), &int32Array));
struct WIDGET_INFO
{
INT32 count;
INT32 maximum;
};
wil::unique_cotaskmem_array_ptr<WIDGET_INFO> infoArray;
RETURN_IF_FAILED(widgetManager->GetWidgetInfos(infoArray.size_address<DWORD>(), &infoArray));
// Wrong! This will leak the strings.
// Use wil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_string> instead.
wil::unique_cotaskmem_array_ptr<PWSTR> stringArray; // WRONG!
// Wrong! This will leak the string handles.
// Use wil::unique_cotaskmem_array_ptr<wil::unique_hstring> instead.
wil::unique_cotaskmem_array_ptr<HSTRING> stringArray; // WRONG!
// Wrong! This will leak the pointers.
// Use wil::unique_cotaskmem_array_ptr<unique_widget_item> instead (defined below).
struct WIDGET_ITEM
{
PWSTR name;
IWidget* widget;
};
wil::unique_cotaskmem_array_ptr<WIDGET_ITEM> itemArray; // WRONG!
// Wrong! This will not run the class destructor.
// Do not use this pattern at all.
class Widget
{
public:
~Widget(); // destructor is present
};
wil::unique_cotaskmem_array_ptr<Widget> widgetArray; // WRONG!
// Wrong! This will leak the COM references. The ComPtr destructor does NOT run!
// Use wil::unique_cotaskmem_array_ptr<wil::com_ptr<T>> instead.
wil::unique_cotaskmem_array_ptr<Microsoft::WRL::ComPtr<IInspectable>> valuesArray; // WRONG!
For unique_any
-derived types, unique_struct
-derived types, and
com_ptr
, the elements are destructed according to the rules for that
type, and then the entire array is freed with CoTaskMemFree
.
wil::unique_cotaskmem_array_ptr<wil::unique_string> stringArray;
RETURN_IF_FAILED(propertyValue->GetStringArray(stringArray.size_address<UINT32>(), &stringArray));
wil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_string> stringArray;
RETURN_IF_FAILED(widgetManager->GetWidgetNames(&stringArray, stringArray.size_address<DWORD>()));
wil::unique_cotaskmem_array_ptr<wil::unique_variant> variantArray;
RETURN_IF_FAILED(widgetManager->GetValues(variantArray.size_address<DWORD>(), &variantArray));
wil::unique_cotaskmem_array_ptr<wil::com_ptr<IInspectable>> valuesArray;
RETURN_IF_FAILED(propertyValue->GetInspectableArray(valuesArray.size_address<UINT32>(), &valuesArray));
If the elements of the array require individual destruction, create a
custom unique_any
or unique_struct
as necessary.
struct WIDGET_ITEM
{
PWSTR name;
IWidget* widget;
};
void DestroyWidgetItem(WIDGET_ITEM* item)
{
CoTaskMemFree(item->name);
if (item->widget) item->widget->Release();
}
using unique_widget_item = wil::unique_struct<WIDGET_ITEM, decltype(&::DestroyWidgetItem), ::DestroyWidgetItem>;
wil::unique_cotaskmem_array_ptr<unique_widget_item> itemArray;
Basic usage is covered in the sample below. See the references for
std::unique_ptr
and for std::array
for the complete rundown of the interfaces that unique_any_array_ptr
is modeled after.
// Construct a new smart pointer with an array and size
wil::unique_cotaskmem_array_ptr<wil::unique_handle> ptr(handleArray, handleArraySize);
// Walk the array
for (auto& handle : ptr)
{
// code...
}
// Iterators
auto it = ptr.begin();
auto itEnd = ptr.end();
// Retrieve the array
auto resource = ptr.get();
// Check validity of the array (allocated)
if (ptr)
{
// resource is assigned
}
// Get the number of elements
auto size = ptr.size();
// Accessors
ptr[index];
ptr.front();
ptr.back();
// Check if there are no elements in the array (size zero)
if (ptr.empty())
{
// array is empty (may be allocated)
}
// Free the array
ptr.reset();
// Free and replace the array
ptr.reset(handleArray, handleArraySize);
// Detach array from the pointer without freeing
auto resource = ptr.release();
// Return the address of the internal resource for out parameter use
// Note: Also frees any currently held resource
WindowsApiCall(&ptr, ptr.size_address());
// Note: Template specialization is required on size_address when it's not size_t.
WindowsApiCall(&ptr, ptr.size_address<DWORD>());
// Same as two previous
WindowsApiCall(ptr.put(), ptr.size_address<DWORD>());
// Swap resources between smart pointers
ptr.swap(ptr2);
Sometimes raw structures returned as part of ABI contracts require the owner to explicitly manage the lifetime of one or more members before the structure may be destroyed.
WIL's unique_struct
can be used to handle these scenarios. Using it
allows you to overlay the std::unique_ptr contract over any raw
structure while retaining nearly the exact same usage characteristics as
the raw structure itself.
Take the following example; PROCESS_INFORMATION is a raw structure returned from CreateProcess that requires the caller to explicitly close the process and thread handles when finished with them:
typedef struct {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
This can then be wrapped up with a function to close the handles and a typedef to wrap the PROCESS_INFORMATION type with a unique_struct:
namespace details
{
inline void __stdcall CloseProcessInformation(PROCESS_INFORMATION* info)
{
CloseHandle(info->hProcess);
CloseHandle(info->hThread);
}
}
using unique_process_information = unique_struct<PROCESS_INFORMATION, decltype(&details::CloseProcessInformation), details::CloseProcessInformation>;
This newly defined unique_process_information can then be used interchangeably with PROCESS_INFORMATION, the only differences being default-zero, automatic cleanup (destructor) and the unique_ptr interface:
unique_process_information process;
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(..., CREATE_SUSPENDED, ..., &process));
THROW_IF_WIN32_BOOL_FALSE(ResumeThread(process.hThread));
THROW_LAST_ERROR_IF_FALSE(WaitForSingleObject(process.hProcess, INFINITE) == WAIT_OBJECT_0);
// reset_and_addressof() closes the handles before returning the address.
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(..., &process.reset_and_addressof()));
// process.hThread and process.hProcess will be closed when the process variable goes out of scope
Note that we used the reset_and_addressof()
method
to free the previous contents before passing it to a function
that writes new contents.
By default, unique_struct initializes the structure using ZeroMemory. If the structure needs non-zero invalid state (such as a cbSize value that needs initialized) then a custom initialization function can also be supplied as a template parameter (in exactly the same way as the custom close method).
They key differences from wil::unique_any
are:
- The smart pointer type derives from the raw structure, rather than containing it (is-a relationship)
- The '&' operator does NOT free the internal resource due to frequent
INOUT use of structures in ABI contracts. Use the
reset_and_addressof()
method when that behavior is desired.
Basic usage is covered in the sample below. See the reference for std::unique_ptr for the complete rundown of the interface that unique_struct is modeled (nearly identically) after.
// Construct a zero initialized structure
wil::unique_process_information ptr;
// Reference structure member variables
if (ptr.hProcess && ptr.hThread) {}
// Free the resource and re-initializes to zero
ptr.reset();
// Detach structure from being managed without freeing the resource
auto value = ptr.release();
// Return the address of the internal resource for in-out parameter use
// Note: Does NOT free the internally held resource
WindowsApiCall(&ptr);
// Return the address of the internal resource for out parameter use
// Note: Also frees any currently held resource
WindowsApiCall(ptr.reset_and_addressof());
// Swap resources between smart pointers
ptr.swap(ptr2);
The unique_struct
is exactly the same size as the wrapped type.
This allows an array of unique_struct<T>
to be used as if it
were an array of T
.
There are fewer predefined unique_struct
s than other WIL unique types
because structs that need to be freed (or contain items that need to be
freed) are usually particular to specific COM ABI contracts. Locally
defined unique_struct
instances are more common.
The following are publicly defined types already covered by WIL's resource header. If you need a different type, search WIL's Resource.h for the type you need; if you don't find it, consider adding it:
Type | Wrapped object | Cleanup | Notes |
---|---|---|---|
unique_multi_qi |
MULTI_QI |
Release(this->pItf) |
|
unique_process_information |
PROCESS_INFORMATION |
CloseHandle(this->hProcess), CloseHandle(this->hThread) |
|
unique_prop_variant |
PROPVARIANT |
PropVariantClear |
|
unique_stg_medium |
STGMEDIUM |
ReleaseStgMedium |
|
unique_token_linked_token |
TOKEN_LINKED_TOKEN |
CloseHandle(this.LinkedToken) |
|
unique_variant |
VARIANT |
VariantClear |
To support usage of WIL in places where the STL
std::unique_ptr
is not available, WIL defines wistd::unique_ptr
. It is identical to
std::unique_ptr
in every way except it can be used in exception-free
code and lives in a different namespace.
If you are able to use std::
, prefer using that over wistd::
.
Windows exposes many different allocators that are used across ABI
boundaries. WIL's Resource.h aims to includes specializations of
unique_ptr
for all commonly used specialized allocators.
For example, the following type definition sets up a generic cotaskmem
unique_ptr
:
template <typename T>
using unique_cotaskmem_ptr = wistd::unique_ptr<T, cotaskmem_deleter>;
This then allows callers to declare a typed cotaskmem-allocated pointer
and manage it through the unique_ptr
contract:
wil::unique_cotaskmem_ptr<ITEMIDLIST_ABSOLUTE> idList;
RETURN_IF_FAILED(SHGetIDListFromObject(item, wil::out_param(idList)));
As of this writing, there are type definitions for over a dozen specialized allocators that are covered by WIL's resource header:
unique_process_heap_ptr
unique_hlocal_ptr
unique_hlocal_secure_ptr
unique_hglobal_ptr
unique_wtsmem_ptr
unique_ncrypt_ptr
unique_bcrypt_ptr
unique_cotaskmem_ptr
unique_cotaskmem_secure_ptr
unique_lsa_ptr
unique_wcm_ptr
unique_wlan_ptr
unique_midl_ptr
Note that some of these pointers are 'secure' pointers; these are merely
those that have been enlightened with SecureZeroMemory
when the memory
has been freed. These are used to mitigate some forms of security
attack.
When managing memory with wistd::unique_ptr
, best practice is to
avoid calling new explicitly
(R.11).
This is typically done through the use of std::make_unique
(C.150).
To support these principles with wistd::unique_ptr
, WIL exposes
wil::make_unique_nothrow
and wil::make_unique_failfast
:
template <class T, class... Types>
inline wistd::unique_ptr<T> make_unique_nothrow(Types&&... args);
template <class T, class... Types>
inline wistd::unique_ptr<T> make_unique_failfast(Types&&... args);
Example use:
// Returns a smart pointer holding 'null' on allocation failure
auto foo = wil::make_unique_nothrow<Foo>(fooConstructorParam1, fooConstructorParam2);
RETURN_IF_NULL_ALLOC(foo);
foo->Bar();
// Fail-fasts the process on allocation failure
auto foo = wil::make_unique_failfast<Foo>(fooConstructorParam1, fooConstructorParam2);
foo->Bar();
Some of the most commonly used allocators also have similar helpers:
make_unique_hlocal_nothrow
, make_unique_hlocal_failfast
,
make_unique_hlocal
, make_unique_cotaskmem_nothrow
,
make_unique_cotaskmem_failfast
, make_unique_cotaskmem
,
make_unique_cotaskmem_secure_nothrow
,
make_unique_cotaskmem_secure_failfast
,
make_unique_cotaskmem_secure
,
make_unique_hlocal_secure_nothrow
,
make_unique_hlocal_secure_failfast
, make_unique_hlocal_secure
WIL's unique_any
works great when there is a token (or handle) that
can be freed through calling a global method that can accept that token
to release the resource. WIL's unique_com_token
comes into play when
the token belongs to a COM interface that must be used to release the
token.
To enable the token to be freed from the given interface,
unique_com_token
requires both the token and the interface pointer and
will hold a strong reference to the interface.
Take for example, the DWORD
token both retrieved and freed from
methods on the IConnectionPoint
interface:
STDMETHOD(Advise)(IUnknown* sink, _Out_ DWORD* cookie) PURE;
STDMETHOD(Unadvise)(DWORD cookie) PURE;
To use unique_com_token
on this token, you would first define a
function that accepts the interface and can free the given
token:
inline void __stdcall IConnectionPointUnadvise(IConnectionPoint* source, DWORD cookie)
{
source->Unadvise(cookie);
}
This function is then used to define a smart pointer to manage the interface token:
using unique_connection_point_cookie =
unique_com_token<IConnectionPoint, DWORD, decltype(IConnectionPointUnadvise), IConnectionPointUnadvise>;
// It's also possible to directly pass the interface method,
// avoiding the need to write a function wrapping the member call.
using unique_connection_point_cookie =
unique_com_token<IConnectionPoint, DWORD, decltype(&IConnectionPoint::Unadvise), &IConnectionPoint::Unadvise>;
Using the newly defined type:
wil::unique_connection_point_cookie cookie;
cookie.associate(connection_point);
THROW_IF_FAILED(connection_point->Advise(sink, &cookie));
Basic usage is covered in the sample below. Semantically, the usage matches unique_any almost identically with the addition of additionally managing the owning interface.
// Construct a new pointer with an existing token
wil::unique_connection_point_cookie registration(existing_source, existing_cookie);
// Retrieve the token
auto cookie = registration.get();
// Check validity of the token
if (registration)
{
// token is assigned
}
// Free the current token and take a strong reference to a new source
// Note: Works to prepare for use with the '&' operator
registration.associate(new_source);
// Releases token and the source
registration.reset();
// Free and replace the token, preserving the source
registration.reset(new_token);
// Free and replace both the source and the token
registration.reset(new_source, new_token);
// Detach token from the registration without freeing.
// Note: Requires caller to have an existing reference to the source.
auto resource = registration.release();
// Return the address of the internal token for out parameter use
// Note: Also frees any currently held token; requires previous associate call.
WindowsApiCall(&resource);
// Same as previous
WindowsApiCall(resource.put());
// Return the address of the internal token for in-out parameter use
// Note: Requires previous associate call.
WindowsApiCall(ptr.addressof());
// Swap resources
registration.swap(registration2);
WIL's unique_com_token
works great when there is a token (or handle)
that can be freed using a given COM interface. WIL's unique_com_call
can be used when there is no token, but simply a method that needs to be
called on a COM interface to complete an operation.
To enable the call to be made from the given interface,
unique_com_call
holds a strong reference to the interface.
Take for example, the Close
method on the IClosable
interface. To
use unique_com_call
to make the call, you first define a function that
accepts the interface and can make the call:
inline void __stdcall CloseIClosable(IClosable* source)
{
source->Close();
}
This function is then used to define a smart pointer to manage the interface:
using unique_closable_call = unique_com_call<IClosable, decltype(CloseIClosable), CloseIClosable>;
// same as unique_com_token
using unique_closable_call = unique_com_call<IClosable, decltype(&IClosable::Close), &IClosable::Close>;
Using the newly defined type:
wil::com_ptr<IRandomAccessStream> randomAccessStream;
THROW_IF_FAILED(IStorageFile_OpenAsync(file.Get(), FileAccessMode_Read, &randomAccessStream));
auto closable = wil::com_query<IClosable>(randomaccessStream);
wil::unique_closable_call close(closable.get());
// code ...
// "close" destructor calls IClosable::Close.
Basic usage is covered in the sample below. Semantically, the usage matches unique_any almost identically. The only difference is that this class manages the call that should be made on the interface, rather than the interface itself, so you can't inspect or detach the COM pointer separately.
// Construct a new call with the source interface
wil::unique_closable_call call(closable.get())
// Check if the call is still outstanding
if (call)
{
// call will still be made
}
// Makes the call ahead of destruction; releases the interface
call.reset();
// Ensures the call will not be made; releases the interface
call.release();
// Return the address of the internal com interface for out parameter use
// Note: Also makes any outstanding calls and release the held interface.
WindowsApiCall(&call);
// Same as previous
WindowsApiCall(call.put());
// Return the address of the internal com interface for in-out parameter use
// Any interface held in the call is leaked.
WindowsApiCall(call.addressof());
// Swap resources
call.swap(call2);
WIL's unique_any
works great when there is a token (or handle) that
can be freed using a global function. WIL's unique_call
can be used
when there is no token, but simply a global function that needs to be
called to complete an operation.
Take for example, the CoUninitialize
method. To use unique_call
to
make the call, you can define the smart pointer type that manages the
call as
follows:
using unique_couninitialize_call = unique_call<decltype(&::CoUninitialize), ::CoUninitialize>;
Simply creating an instance of the type on the stack will ensure the call is made when the type goes out of scope:
RETURN_IF_FAILED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED));
unique_couninitialize_call cleanup;
// code ...
// May use early returns; CoUninitialize will still be called
Once the smart pointer type is in place, it's useful to define helpers that can return it after performing the operation that required the call in the first place:
_Check_return_ inline unique_couninitialize_call CoInitializeEx(DWORD coinitFlags = COINIT_MULTITHREADED)
{
THROW_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags));
return unique_couninitialize_call();
}
That helper can then be used to simplify callers of this pattern:
auto cleanup = wil::CoInitializeEx();
// code...
// May throw exceptions or return early; CoUninitialize will still be called
Basic usage is covered in the sample below. Semantically, the usage
matches
unique_any
almost identically. The only difference is that this class manages a
call that should be made, rather than a resource, so there is nothing to
get()
from the smart pointer.
// Construct a new call
wil::unique_couninitialize_call call;
// Check if the call is still outstanding
if (call)
{
// call will still be made
}
// Makes the call ahead of destruction; will not call twice
call.reset();
// Ensures the call will not be made
call.release();
// Swap resources
call.swap(call2);
Global method contracts are common in some domains, so if those methods are exposed from a public or published header that is shared across multiple teams, you should add your type to Resource.h for inclusion in WIL. Use the same inclusion guard technique and naming pattern described here.
Following are publicly defined types and helpers already covered by WIL's resource header. Search WIL's Resource.h for the call type you need; if you don't find it, consider adding it:
unique_coreverttoself_call
with helpers: wil::CoImpersonateClient
and
wil::CoImpersonateClient_failfast
unique_couninitialize_call
with helpers: wil::CoInitializeEx
and wil::CoInitializeEx_failfast
unique_rouninitialize_call
with helpers: wil::RoInitialize
and wil::RoInitialize_failfast
Consider using wil::scope_exit
to handle unique or uncommon cleanup
cases where RAII techniques are impractical instead of trying to
carefully handle control flow (avoiding early returns and exceptions).
wil::scope_exit
returns an object that executes the given lambda when
the object goes out of scope. This allows the lambda to run in the face
of an early return or thrown exception.
Example:
void MemberFunction()
{
StartMemberFunction();
auto cleanup = wil::scope_exit([&]
{
StopMemberFunction();
});
// Code ...
// (may have early returns or generate exceptions)
// The destructor of the 'cleanup' object will run StopMemberFunction.
}
wil::scope_exit
is a powerful tool, but it can often be replaced with
an existing RAII class or a custom version of one of the classes above.
Before using scope_exit
, consider whether or not the cleanup code
being run is unique to this code site. If the code isn't unique (for
example, calls a global method or COM interface method to release a
resource) then find an existing RAII class (or create a new one from the
primitives available in Resource.h) and use that instead of using
scope_exit. If it's broadly applicable, consider defining it in
Resource.h and sending a pull request!
Basic usage, simply involves defining the lambda and holding onto it in a local variable:
auto cleanup = wil::scope_exit([&]
{
// cleanup code ...
});
To execute the lambda early, before the scope exit variable is
destroyed, use the reset()
function. Note that the lambda will only
ever execute once if ran early. Example:
StartMemberFunction();
auto cleanup = wil::scope_exit([&]
{
StopMemberFunction();
});
// Code ...
// Run the lambda ahead of destruction of the cleanup variable:
cleanup.reset();
// More Code ...
To avoid executing the lambda at all, call the release()
routine. This
behavior is appropriate for cleanup that's only need in the event of
failure. Example:
HRESULT Start()
{
RETURN_IF_FAILED(GetStarted());
auto cleanup = wil::scope_exit([&]
{
StopEarly();
});
// More code that may have early return failures, generate exceptions, etc.
// Successfully completion - avoid StopEarly call:
cleanup.release();
return S_OK;
}
- Closely associate a scope_exit instance with the resource it is responsible for cleaning up (declare it immediately after). Adding distance between the resource and the scope_exit declaration invites the introduction of unwanted early returns or exceptions between these points.
- By default, throwing an exception from a scope_exit lambda will
result in fail fast. You may also use
scope_exit_log
to log any thrown exception to telemetry without termination.
- Only use default reference capture [&] with any scope_exit lambdas. If by-value capture is used, copy construction could lead to exceptions when the lambda is created.
- Do not over-use scope_exit; use should be uncommon. Prefer more strongly typed RAII classes.
Many smart pointer classes permit access to the address of the inner
pointer, usually by overloading operator&
or by having a method
similar to addressof
. Use those methods if available.
In the case where your smart pointer class does not provide direct
access to the inner pointer (such as wistd::unique_ptr
), the
out_param
method can be used to avoid two-phase construction:
// Do not do this:
// What would previously have been done with two-phase construction...
ITEMIDLIST_ABSOLUTE idListRaw = nullptr;
RETURN_IF_FAILED(propStore.GetAsIDList(PKEY_PropApply_ItemIDList, &idListRaw));
wil::unique_cotaskmem_ptr<ITEMIDLIST_ABSOLUTE> idlist(idListRaw);
// Instead do this:
// Can be done in a single step with wil::out_param...
wil::unique_cotaskmem_ptr<ITEMIDLIST_ABSOLUTE> idlist;
RETURN_IF_FAILED(propStore.GetAsIDList(PKEY_PropApply_ItemIDList, wil::out_param(idlist)));
This helper works through the creation of a temporary object on the stack which receives the resulting pointer and then on its destruction applies that resource to the smart pointer. See warning below.
Occasionally, the output pointer type used in ABIs does not match the
actual underlying type that is allocated by the API. When this condition
occurs, you can use the out_param_ptr
variant to specify the exact
type that you want to satisfy from the API:
wil::unique_cotaskmem_ptr<ITEMID_CHILD> idlistNew;
RETURN_IF_FAILED(ILCloneWithHiddenID(childIdWithHidden.get(), pidhid, wil::out_param_ptr<PIDLIST_RELATIVE*>(idlistNew)));
Warning: You must not refer to the smart pointer later in the same
expression that populates it with wil::out_param
or
wil::out_param_ptr
.
// Do NOT do this...
// out_param and the smart pointer itself cannot both be used in the same expression
if (SUCCEEDED(foo->Bar(wil::out_param(ptr))) && ptr->IsGood()) {}
This is a consequence of the fact that C++ delays the destruction of
temporaries until the end of the "full expression". This means that the
ptr->IsGood()
is evaluated before wil::out_param
's destructor can
populate the smart pointer.
As a workaround, break the statement into two.
// Break up into two statements to ensure out_param initializes ptr before we use it.
if (SUCCEEDED(foo->Bar(wil::out_param(ptr))))
{
if (ptr->IsGood()) {}
}
For further discussion, see the "Footguns" chapter of the std::out_ptr proposal.
This helper detaches a resource contained within a smart pointer class to an optional output pointer parameter. Using this routine provides a simple nullptr test and calls the appropriate detach routine for the given smart pointer type, allowing a simple one-line detach to an optional output parameter.
IFACEMETHODIMP ResourceGenerator::CreateResource(_COM_Outptr_opt_ IResource **createdResource)
{
wil::assign_null_to_opt_param(createdResource);
wil::com_ptr<IResource> resource;
RETURN_IF_FAILED(m_resourceFactory->CreateResource(IID_PPV_ARGS(&resource)));
RETURN_IF_FAILED(MemoizeResource(resource.get()));
wil::detach_to_opt_param(createdResource, resource);
return S_OK;
}
Note that the supported smart pointer types are
Microsoft::WRL::ComPtr
, wil::com_ptr
, any use of wil::unique_any
or wil::unique_struct
, and unique_ptr
.
- Reader-writer locks (SRWLOCK)
- Re-entrant locks (CRITICAL_SECTION)
- Event handles (includes event watcher)
- Mutex handles
- Semaphore handles
- Secure data erasure
- Slim events
- Last-error preservation
- Kernel spin locks
- Kernel events
- WDF resource classes
Ensure that the header that support the handle type and handle close function required for your smart pointer type has been included before including Resource.h.
See the notes on inclusion order in the usage section.