Skip to content

RAII resource wrappers

Raymond Chen edited this page Oct 21, 2024 · 30 revisions

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

Usage

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>

Resource Patterns

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.)
  • wil::unique_any_array_ptr
    • Manages an allocated array of some other raw type that may itself need managed (DWORD, HANDLE, IFoo*, etc.)
  • wil::unique_struct
    • Manages a raw structure that requires some form of cleanup (VARIANT, PROCESS_INFORMATION, etc.)
  • 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.)
  • wil::unique_com_call
    • Manages a call made with a specific COM interface (IClosable::Close, etc.)
  • wil::unique_call
    • Manages a call made on a global method (CoUninitialize, CoRevertToSelf, etc.)
  • wil::scope_exit
    • Manages a caller supplied lambda to be executed

wil::unique_any

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)

Usage

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.

Available unique_any simple memory patterns

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

LocalFree

unique_hlocal_ptr
unique_hlocal_ptr<T[]>

make_unique_hlocal_nothrow(...)
make_unique_hlocal_failfast(...)
make_unique_hlocal(...)

make_unique_hlocal_nothrow<T[]>(size)
make_unique_hlocal_failfast<T[]>(size)
make_unique_hlocal<T[]>(size)

CoTaskMemFree

unique_cotaskmem_ptr
unique_cotaskmem_ptr<T[]>

make_unique_cotaskmem_nothrow(...)
make_unique_cotaskmem_failfast(...)
make_unique_cotaskmem(...)

make_unique_cotaskmem_nothrow<T[]>(size)
make_unique_cotaskmem_failfast<T[]>(size)
make_unique_cotaskmem<T[]>(size)

GlobalFree

unique_hglobal_ptr

(none currently defined)

(none currently defined)

HeapFree(GetProcessHeap())

unique_process_heap_ptr

(none currently defined)

(none currently defined)

VirtualFree(MEM_RELEASE)

unique_virtualalloc_ptr

(none currently defined)

(none currently defined)

WTSFreeMemory

unique_wtsmem_ptr

(none currently defined)

(none currently defined)

NCryptFreeBuffer

unique_ncrypt_ptr

(none currently defined)

(none currently defined)

BCryptFreeBuffer

unique_bcrypt_ptr

(none currently defined)

(none currently defined)

LsaFreeReturnBuffer

unique_lsa_ptr

(none currently defined)

(none currently defined)

LsaFreeMemory

unique_lsamem_ptr

(none currently defined)

(none currently defined)

LsaLookupFreeMemory

unique_lsalookupmem_ptr

(none currently defined)

(none currently defined)

WcmFreeMemory

unique_wcm_ptr

(none currently defined)

(none currently defined)

WlanFreeMemory

unique_wlan_ptr

(none currently defined)

(none currently defined)

MIDL_user_free

unique_midl_ptr

(none currently defined)

(none currently defined)

SecureZeroMemory + LocalFree

unique_hlocal_secure_ptr
unique_hlocal_secure_ptr<T[]>

make_unique_hlocal_secure_nothrow(...)
make_unique_hlocal_secure_failfast(...)
make_unique_hlocal_secure(...)

make_unique_hlocal_secure_nothrow<T[]>(size)
make_unique_hlocal_secure_failfast<T[]>(size)
make_unique_hlocal_secure<T[]>(size)

SecureZeroMemory + CoTaskMemFree

unique_cotaskmem_secure_ptr
unique_cotaskmem_secure_ptr<T[]>

make_unique_cotaskmem_secure_nothrow(...)
make_unique_cotaskmem_secure_failfast(...)
make_unique_secure_cotaskmem(...)

make_unique_cotaskmem_secure_nothrow<T[]>(size)
make_unique_cotaskmem_secure_failfast<T[]>(size)
make_unique_secure_cotaskmem<T[]>(size)

  • The make_unique_xxx methods forward the ... to the T constructor.
  • The make_unique_xxx<T[]> methods allocate an array of size objects of type T with the default constructor.

Available unique_any handle types

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 where nullptr is the null state, while unique_hfile is for file handles where INVALID_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 be unique_hlocal_sid, as it frees the SID with LocalFree and there is nothing about SIDs that requires them to be allocated with LocalAlloc.

  • unique_event has a lightweight variant wil::slim_event. See below for details.

  • unique_hdc frees the DC with DeleteDC, unique_hdc_window frees the DC with ReleaseDC (and the window handle is available from the hwnd member), and unique_hdc_paint frees the DC with EndPaint (and the window and PAINTSTRUCT are available in the hwnd and ps members).

  • unique_wdf_spin_lock and unique_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.

Defining a new unique_any type

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 definitions

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 is pointer_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 to nullptr_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.

Using shared_any, a reference counted version of unique_any

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>;

wil::unique_any_array_ptr

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 HSTRINGs and provides the deleter required to free the HSTRINGs 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;

Usage

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);

wil::unique_struct

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.

Usage

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.

Available unique_struct resource types

There are fewer predefined unique_structs 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

wistd::unique_ptr

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::.

Supporting types

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.

Supporting helpers

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::unique_com_token

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));

Usage

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::unique_com_call

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.

Usage

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::unique_call

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

Usage

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);

Available unique_call types and helpers

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

wil::scope_exit

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!

Usage

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;
}

Best practices

  • 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.

Resource Helpers

wil::out_param

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.

wil::detach_to_opt_param

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.

Wrappers and helpers for specific resources

Usage Issues

Identifier not found compilation error referencing pick_your_smart_pointer

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.

Clone this wiki locally