potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

WIL GitHub Wiki RAIIリソースラッパーの和訳・改変

2022/9/19:2020/8/25版→2022/3/5版に修正しました(そしてミスをしました)。

MicrosoftがGitHubでMITライセンス公開しているWILのGitHub WikiからRAIIリソースラッパーの記事を和訳・改変したものです。素人による翻訳なので、誤訳や著作権上の問題などありましたらご連絡いただけますと幸いです。

一部のリンク切れは意図的なものです。原文をご参照ください。

RAIIリソースラッパー

Raymond Chem、2022/3/5、リビジョン26個

WILリソースラッパーはC++開発者がすべてのコードで一貫してRAIIを使用できるようにデザインされたスマートポインタパターンとリソースラッパーのファミリを提供します。これらのパターンはWindowsコードベースで広範に使用されており、OSの多様なリソース型に応じた100以上の異なるRAIIリソースクラスを伴います。ゴールは以下の提供です。

  • すべての適切なWindowsハンドル、リソース、ロック型の統一的で一貫したRAII(スマートポインタ)。
  • クリーンアップパターンを一般に要求する統一された新しいRAIIラッパーの作成を簡単にする機能。
  • OSコンセプトに沿った単純で使いやすいラッパー。

使い方

このリソースラッパーライブラリはResource.hの相対インクルードにより任意のユーザーモードC++コードで使用できます。

#include <wil/resource.h>

Resource.hはインクルード前に定義された型のラッパーのみ定義することに注意してください。このヘッダーはどんなシステムヘッダーもインクルードしません。言い換えれば、あるヘッダーファイルで定義される型にResource.hを使用する場合、そのヘッダーファイルをResource.hの前にインクルードする必要があります。

例えばunique_hinternetを使用する場合、Resource.hの前にWinINet.hをインクルードします。

#include <WinINet.h>
#include <wil/resource.h>

Resource.hは複数回インクルードしても安全です。インクルードの度、Resource.hの前にインクルードしたヘッダーファイルで定義される新しい型のラッパーが定義されます。

ラップした型を含むヘッダーに加えて、新しいスマートポインタ型を作成するためにWILは次のヘッダーに依存します。

リソース 前提条件
wil::weak_* <memory>
wil::shared_* <memory>
wil::make_*_nothrow <new>

リソースパターン

WIL RAIIのすべてのインターフェイス/リソース管理クラスはstd::unique_ptrをモデルにしています。リソースへのアクセスはget()、リソースの解放と置換はreset()、管理クラスからの生リソースの分離はrelease()を使用します。

WILリソースパターンの概要:

  • wil::unique_any
    • Opaqueハンドル型(HANDLEHKEYPSECURITY_DESCRIPTOR等)の管理。
  • wil::unique_any_array_ptr
    • 要素も管理すべき生の型の配列の管理(DWORDHANDLEIFoo*等)。
  • wil::unique_struct
    • ある形式のクリーンアップを要求する生構造体の管理(VARIANTPROCESS_INFORMATION等)。
  • wistd::unique_ptr
  • wil::unique_com_token
  • wil::unique_com_call
  • wil::unique_call
    • グローバルメソッドの呼び出し(CoUninitializeCoRevertToSelf等)の管理。
  • wil::scope_exit
  • ラムダ式として与えられた関数呼び出しの管理。

wil::unique_any

WILのunique_anyは任意のOpaqueリソース型の保持に特化したtraitsベースのスマートポインタです。std::unique_ptr(呼び出し側により多様な特殊化を保持する)とは異なり、unique_anyWindowsがABIレベルで公開する多くのユニークなハンドル型やトークン型のそれぞれに対して専用にtypedefして使用されます。

WILのResource.hにおけるLSA_HANDLEのtypedefの簡単な例を示します。

using unique_hlsa = wil::unique_any<LSA_HANDLE, decltype(&::LsaClose), ::LsaClose>;

ここで、LSA_HANDLEを扱いたい任意の呼び出し側はunique_ptrをモデルとしたインターフェイスのRAIIパターンを使用できます:

unique_hlsa hlsa;
RETURN_IF_NTSTATUS_FAILED(LsaOpenPolicy(nullptr, &oa, POLICY_LOOKUP_NAMES, &hlsa));
RETURN_IF_FAILED(GetProfileSid(hlsa.get(), username, &sidString)))

std::unique_ptrとの主要な違いは以下の通りです。

  • 出力引数利用のために&演算子put()メンバーメソッドを公開する(これらの呼び出しは呼び出し時に所有するリソースを解放する)。
  • 非ポインタリソース型(DWORDトークン等)をサポートする。
  • 非null無効値(INVALID_HANDLE_VALUE)をサポートする。

使い方

基本的な使い方は以下のサンプルがカバーしています。unique_anyがモデルとしている(ほとんど同一の)インターフェイスの完全な説明はstd::unique_ptrのリファレンスを参照してください。さらなる情報はRAIIリソースラッパーの詳細を参照してください。

// リソースで新しいポインタを初期化する。
wil::unique_handle ptr(handle);

// リソースを取得する。
auto resource = ptr.get();

// リソースが有効かチェックする。
if (ptr)
{
    // リソースは割り当て済み。
}

// 上と同じ。
if (ptr.is_valid())
{
    // リソースは割り当て済み。
}

// リソースを解放する。
ptr.reset();

// リソースを解放して置換する。
ptr.reset(handle);

// リソースを解放せずにポインタを分離する。
auto resource = ptr.release();

// 出力引数利用のために内部リソースのアドレスを返す。
// 注意:呼び出し時に保持していたリソースは解放されます。
WindowsApiCall(&ptr);

// 上と同じ。
WindowsApiCall(ptr.put());

// 入出力引数利用のために内部リソースのアドレスを返す。
WindowsApiCall(ptr.addressof());

// スマートポインタ同士のリソースを交換する。
ptr.swap(ptr2);

有効なunique_any単純メモリパターン

多くのリソースはLocalFreeのような共通のクリーンアップパターンを使用します。以下のunique_anyテンプレート型はクリーンアップ関数がよく知られている場合に使用できます。

注意:型Ttrivially destructibleな必要があります。

クリーンアップ関数 ジェネリック スカラー生成関数 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)
HeapFree(GetProcessHeap()) unique_process_heap_ptr (現在無定義) (none currently defined)
VirtualFree(MEM_RELEASE) unique_virtualalloc_ptr (現在無定義) (none currently defined)
WTSFreeMemory unique_wtsmem_ptr (現在無定義) (none currently defined)
NCryptFreeBuffer unique_ncrypt_ptr (現在無定義) (none currently defined)
BCryptFreeBuffer unique_bcrypt_ptr (現在無定義) (none currently defined)
LsaFreeReturnBuffer unique_lsa_ptr (現在無定義) (none currently defined)
LsaFreeMemory unique_lsamem_ptr (現在無定義) (none currently defined)
LsaLookupFreeMemory unique_lsalookupmem_ptr (現在無定義) (none currently defined)
WcmFreeMemory unique_wcm_ptr (現在無定義) (none currently defined)
WlanFreeMemory unique_wlan_ptr (現在無定義) (none currently defined)
MIDL_user_free unique_midl_ptr (現在無定義) (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_xxxメソッドは...Tのコンストラクタに転送します。
  • make_unique_xxx<T[]>メソッドはデフォルトコンストラクタを使用して要素数sizeTの配列を割り当てます。

有効なunique_anyハンドル型

WILのunique_anyスマートポインタは典型的なWindowsリソース型を含むことができます。例えばファイルハンドル(unique_hfile)、同期プリミティブ(unique_eventunique_semaphoreunique_mutex)等です。WILのunique_anyは各型を操作するメソッドを含む特殊化が容易です。

WILはWindowsコードベースのユニークなリソース型の多くをカバーしています。必要な型はWILのResource.hから検索してください。見つけられない場合、あるいはunique_xxx_ptrが用意されていない場合はそのスマートポインタの追加、この表への記述、プルリクエストの送信を検討してください。

ラップするオブジェクト クリーンアップ 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

上表の一部の型の注意:

  • unique_handlenullptrを空状態とするカーネルハンドルに使われます。一方、unique_hfileINVALID_HANDLE_VALUEを空状態とするファイルハンドルに使われます。
  • スレッドプールハンドル(unique_threadpool_*)について、サフィックスのない型名は解放をキャンセルして任意の未解決コールバックを完了まで待ちます。_nocancelサフィックスは解放を待機します。_nowaitサフィックスはキャンセルも待機もしません。
  • unique_hlocal_sidunique_any_psidのより慣用的な名前です。これはSIDをLocalFreeで解放して、必要ならLocalAllocで割り当てます。
  • unique_eventwil::slim_eventの軽量版です。詳細は後述します。
  • unique_hdcDeleteDCでDCを解放します。unique_hdc_windowReleaseDCでDCを解放します(ウィンドウハンドルはhwndメンバーが利用できます)。unique_hdc_paintEndPaintでDCを解放します(ウィンドウハンドルとPAINTSTRUCThwndpsメンバーが利用できます)。
  • unique_wdf_spin_lockunique_wdf_wait_lockはスコープから出るときに自動的に解放されるRAIIロックガードオブジェクトを返すロックメソッドを提供します。詳細はWDF Resource Classesを参照してください。

新しいunique_any型を定義する

新しいパブリックなRAII型の定義、プライベート型のRAIIカバーのためにunique_anyを使用できます。以下の簡単な例に従うことで新しいunique_any型を定義できます。

typedef unique_any<SC_HANDLE, decltype(&::CloseServiceHandle), ::CloseServiceHandle> unique_schandle;

必要な要素はハンドル型(SC_HANDLE)、そのハンドル型の解放に使用する関数の型(decltype(&::CloseServiceHandle))、その関数への実際の関数ポインタ(::CloseServiceHandle)です。まれな需要ですが、「無効」値が0(またはnull)ではないハンドル型も特殊化できます。この場合はResource.hのunique_socketを参照してください。

アドバンスな定義

unique_anyのアドバンスな使い方によりラップしたポインタへのアクセス、保持する型、無効値をカスタマイズできます。

以下にTLSインデックスのアドバンスな使い方を示します。ここではポインタの替わりにDWORDを保持する型として、無効値は0ではなくTLS_OUT_OF_INDEXESとしています。

typedef unique_any<DWORD, decltype(&::TlsFree), ::TlsFree, details::pointer_access_all, DWORD, TLS_OUT_OF_INDEXES, DWORD> unique_tls;

テンプレート引数は以下の通りです。

  • DWORD:ラップする値の基底型。
  • decltype(&::TlsFree), ::TlsFree:ラップした値の解放に呼び出す関数。
  • details::pointer_access_all:ラップした値のアクセス制御。既定値はpointer_access_allですが、後続するテンプレート引数をカスタマイズする場合は省略できません。有効なアクセスレベルは以下を参照してください。
  • DWORD:ラップした値の物理的なストレージ。既定値は最初のテンプレート引数であり、ほとんどの場合でそれと同じです。
  • TLS_OUT_OF_INDEX:無効値。既定値は0/nullptr
  • DWORD:無効値の型。ほとんどの場合は最初のテンプレート引数と同じです。既定値はnullptr_tであり、無効値がポインタでない場合にカスタマイズします。

HANDLEをラップする特殊化ヘルパーは以下の通りです.

ヘルパー 既定値 無効値 実例
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;

アクセスレベル

レベル get() release() operator& addressof()
details::pointer_access_all 許可 許可 許可 許可
details::pointer_access_noaddress 許可 許可 拒否 拒否
details::pointer_access_none 拒否 拒否 拒否 拒否

さらなる情報とよりアドバンスな使い方はRAII Resource wrapper detailsを参照ください。

unique_anyの参照カウント版shared_anyを使う

WILはwil::shared_anyにより任意のunique_any型の参照カウント版を提供します。

例えば、WILのResource.hではunique_handleの共有ポインタが定義されています。

using shared_handle = wil::shared_any<unique_handle>;

shared_handleの基本的な使い方はstd::shared_ptrとWILのunique_anyコントラクトの結合です。

shared_ptrとの主要な違いはshared_ptrが例外ベースコントラクトである一方、shared_anyは例外ベースコードのみで使えることです。これは以下の操作でメモリ不足例外の発生を可能としています。

// 以下の任意の操作は例外を発生します。
// 注意:リソースは例外発生時も解放されます。

wil::shared_handle ptr(handle);
ptr.reset(handle);
WindowsApiCall(&ptr);
WindowsApiCall(ptr.put());
WindowsApiCall(ptr.addressof());

似たように、WILはshared_anyに対してstd::weak_ptrのように動作するweak_anyを定義しています。以下が具体例です。

using weak_handle = wil::weak_any<shared_handle>;

wil::unique_any_array_ptr

ABIコントラクトではたまに、割り当てた配列と要素数コントラクトの一部として返し、配列自体を解放する前に各要素の解放を要求する場合があります。

WILのunique_any_array_ptrはカウントと割り当てられた要素の配列へのポインタを管理することでそのような状況の処理に使用できます。配列と各要素はそれぞれカスタムデリーターが解放するため、例えばcotaskmemに割り当てられたハンドルの配列等を管理できます。配列は各要素を閉じてから解放されます。

unique_any_array_ptrを使うには以下のように配列が保持する型、配列と要素それぞれのデリーターが必要です。

template <typename ValueType, typename ArrayDeleter, typename ElementDeleter = empty_deleter>
class unique_any_array_ptr

WILは任意のWILスマートポインタから配列要素の型とデリーターを提供する型traitsを簡単に使うためのunique_array_ptrを定義しています。これは主に配列のアロケーターとして定義される型定義(unique_comtaskmem_array_ptr等)を介して使用されます。

template <typename T, typename ArrayDeleter>
using unique_array_ptr

template <typename T>
using unique_cotaskmem_array_ptr = unique_array_ptr<T, cotaskmem_deleter>;

現実世界の実例です。

wil::unique_cotaskmem_array_ptr<wil::unique_hstring> stringArray;
THROW_IF_FAILED(propVal->GetStringArray(stringArray.size_address<UINT32>(), &stringArray));

wil::unique_hstringの使用は型traitsのためです。これはcotaskmem配列をHSTRINGの生割当配列として特定して、配列の解放時にHSTRINGの解放で必要なデリーターを提供します。配列ポインタとサイズは一度の呼び出しでセットされることに注意してください。この関数はその大きさにsize_tを使用しませんが、テンプレート化されたsize_addressルーチンはルーチンで適切な型を特殊化するために使用されます。

unique_array_ptrがサポートするスマートポインタ型は以下のとおりです。

  • POD型(単純なデータ型)
  • unique_any
  • unique_struct
  • com_ptr

POD型では、配列はCoTaskMemFreeにより解放されます。個々の要素は解放されません。オブジェクトに特殊なクリーンアップが必要な場合、unique_anyunique_structの派生型を使用してください(以下を参照)。

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

// 間違え!文字列がメモリリークします。
// 代わりにwil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_string>を使います。
wil::unique_cotaskmem_array_ptr<PWSTR> stringArray; // 間違え!

// 間違え!文字列ハンドルがメモリリークします。
// 代わりにwil::unique_cotaskmem_array_ptr<wil::unique_hstring>を使います。
wil::unique_cotaskmem_array_ptr<HSTRING> stringArray; // 間違え!

// 間違え!ポインタがメモリリークします。
// 代わりにwil::unique_cotaskmem_array_ptr<unique_widget_item>を使います(以下で定義)。
struct WIDGET_ITEM
{
   PWSTR name;
   IWidget* widget;
};
wil::unique_cotaskmem_array_ptr<WIDGET_ITEM> itemArray; // 間違え!

// 間違え!クラスのデストラクタが呼び出されません。
// このパターンは使わないでください。
class Widget
{
public:
  ~Widget(); // デストラクタがある。
};
wil::unique_cotaskmem_array_ptr<Widget> widgetArray; // 間違え!

// 間違え!COMリファレンスがメモリリークします。ComPtrのデストラクタが呼び出されません。
// 代わりにwil::unique_cotaskmem_array_ptr<wil::com_ptr<T>>を使います。
wil::unique_cotaskmem_array_ptr<Microsoft::WRL::ComPtr<IInspectable>> valuesArray; // 間違え!

unique_any派生型、unique_struct派生型、com_ptrについて、各要素はその型の規則に従って解放されます。その後、配列自体は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));

配列の要素が個別の解放を必要とする場合、カスタムunique_anyまたはunique_structの作成が必要です。

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;

使い方

基本的な使い方は以下でカバーされています。unique_any_array_ptrstd::arrayインターフェイスをモデルとしているため、詳細はstd::unique_ptrstd::arrayを参照してください。

// 配列とサイズから新しいスマートポインタを作成する。
wil::unique_cotaskmem_array_ptr<wil::unique_handle> ptr(handleArray, handleArraySize);

// 配列をウォークする。
for (auto& handle : ptr)
{
    // code...
}

// イテレーター
auto it = ptr.begin();
auto itEnd = ptr.end();

// 配列を取得する。
auto resource = ptr.get();

// 配列が有効か(割り当て済みか)のチェック。
if (ptr)
{
    // リソースは割り当て済み。
}

// 要素数の取得。
auto size = ptr.size();

// アクセサー
ptr[index];
ptr.front();
ptr.back();

// 配列が空かチェックする。
if (ptr.empty())
{
    // 配列は空(おそらく割り当て済み)
}

// 配列の解放
ptr.reset();

// 配列の解放と置換
ptr.reset(handleArray, handleArraySize);

// ポインターを解放せずに配列をデタッチする。
auto resource = ptr.release();

// 内部リソースのアドレスを出力引数で使用するために返す。
// 注意:現在のリソースは解放されます。
WindowsApiCall(&ptr, ptr.size_address());

// 注意:size_tを使用しない場合、size_addressはテンプレート特殊化が必要。
WindowsApiCall(&ptr, ptr.size_address<DWORD>());

// 上のふたつと同じ。
WindowsApiCall(ptr.put(), ptr.size_address<DWORD>());

// スマートポインタ同士のリソース交換。
ptr.swap(ptr2);

wil::unique_struct

ABIコントラクトの一部として返される生の構造体はしばしば構造体の解放前にひとつ以上のメンバーのライフタイムを明示的に管理するオーナーを必要とします。

WILのunique_structはこのシナリオに対処するために使用できます。生の構造体の使い勝手をほぼ保ったまま、std::unique_ptrコントラクトでオーバーレイできます。

以下の例をみてください。PROCESS_INFORMATIONCreateProcessが返す生の構造体であり、使用完了時にプロセスとスレッドのハンドルを明示的に閉じることを呼び出し側に要求します。

typedef struct {
    HANDLE hProcess;
    HANDLE hThread;
    DWORD dwProcessId;
    DWORD dwThreadId;
} PROCESS_INFORMATION;

これはハンドルを閉じる関数をラップしてunique_structのtypedefでPROCESS_INFORMATION型をラップすることで対応できます。

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

新しく定義されたunique_process_informationPROCESS_INFORMATIONと交換可能です。違いは初期値が0、自動的なクリーンアップ(デストラクタ)、unique_ptrインターフェスだけです。

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()はアドレスを返す前にハンドルを閉じます。
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(..., &process.reset_and_addressof()));

// process.hThreadとprocess.hProcessはスコープから出るときに解放されます。

新しい内容を書き込む関数に渡す前にreset_and_addressof()メソッドで以前の内容を解放していることに注意してください。

既定ではunique_structZeroMemoryにより構造体を初期化します。もしも非0無効化状態が必要な場合(初期化時にcbSize値が必要な場合等)はテンプレート引数にカスタム初期化関数を指定してください(カスタム解放メソッドとまったく同じ方法です)。

wil::unique_anyとの主要な違いは次の通りです。

  • スマートポインタ型は生の構造体から派生します。含むのではありません(is-a関係)。
  • ABIコントラクトでは構造体の入出力が頻繁であるため、&演算子は内部リソースを解放しません。必要に応じてreset_and_addressof()メソッドを使用してください。

使い方

基本的な使い方は以下のサンプルがカバーしています。unique_structstd::unique_ptrインターフェイスをモデルとしている(ほぼ同一)なため、詳細はstd::unique_ptrを参照してください。

// ゼロ初期化された構造体を作成する。
wil::unique_process_information ptr;

// 構造体メンバーを参照する。
if (ptr.hProcess && ptr.hThread) {}

// リソースを開放して再ゼロ初期化する。
ptr.reset();

// リソースを解放せずに管理している構造体をデタッチする。
auto value = ptr.release();

// 入出力引数で使うために内部リソースのアドレスを返す。
// 注意:内部リソースは解放されません。
WindowsApiCall(&ptr);

// 出力引数で使うために内部リソースのアドレスを返す。
// 注意:呼び出し時のリソースは解放されます。
WindowsApiCall(ptr.reset_and_addressof());

// スマートポインタ同士のリソースを交換する。
ptr.swap(ptr2);

unique_structはラップする型と正確に同じサイズです。これによりunique_struct<T>の配列はTの配列と同様に扱えます。

有効なunique_structリソース型

他のWILユニーク型よりも少数のunique_structが定義されています。これは解放が必要な構造体(即ち含まれる要素の解放が必要な構造体)はCOM ABIコントラクトの一部でしか使われないためです。局所的なunique_structインスタンスの定義はより一般的です。

以下はWILリソースヘッダーでカバーされるパブリックに定義された型です。もしも異なる型が必要な場合、WILのResource.hを検索してください。見つからない場合、追加を検討してください。

ラップするオブジェクト クリーンアップ
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

STLstd::unique_ptrが使えない状況でWILの使用を可能とするため、WILはwistd::unique_ptrを定義しています。これは例外フリーコードが使用できること、名前空間が異なることを除いてstd::unique_ptrと同じものです。

std::が使用できる場合、wistd::の使用が推奨されます。

サポートされる型

WindowsはABI境界に渡って多くの異なるアロケータを公開しています。WILのResource.hはすべての一般的に使用される特殊化されたアロケータのunique_ptrによる特殊化のインクルードを目指しています。

例えば、以下の型定義は一般的なcotaskmemのunique_ptrをセットアップします。

template <typename T>
using unique_cotaskmem_ptr = wistd::unique_ptr<T, cotaskmem_deleter>;

これは呼び出し側に型指定されたcotaskmem割り当てポインターの宣言とunique_ptrコントラクトによる管理を可能とします。

wil::unique_cotaskmem_ptr<ITEMIDLIST_ABSOLUTE> idList;
RETURN_IF_FAILED(SHGetIDListFromObject(item, wil::out_param(idList)));

この記述により、WILのリソースヘッダーは多数の特殊化アロケーター型を定義しています。

  • 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

いくつかのポインタは「セキュア」ポインタであることに注意してください。これらは解放時にSecureZeroMemoryが使用されるものに過ぎません。これらはある種のセキュリティ攻撃の軽減に必要です。

サポートされるヘルパー

wistd::unique_ptrでメモリを管理する場合、ベストプラクティスはavoid calling new explicitly (R.11)です。これは典型的にthe use of std::make_unique (C.150)を使用します。wistd::unique_ptrでこの原則をサポートするため、WILはwil::make_unique_nothrowwil::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);

使用例:

// 割り当て失敗時に「null」を持つスマートポインタを返します。
auto foo = wil::make_unique_nothrow<Foo>(fooConstructorParam1, fooConstructorParam2);
RETURN_IF_NULL_ALLOC(foo);
foo->Bar();

// 割り当て失敗時にプロセスをフェイルファストします。
auto foo = wil::make_unique_failfast<Foo>(fooConstructorParam1, fooConstructorParam2);
foo->Bar();

いくつかのよく使われるアロケーターには類似のヘルパーがあります。

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のunique_anyトークン(またはハンドル)がグローバルメソッドの呼び出しにより解放できる場合にうまく機能します。WILのunique_com_tokenトークンがあるCOMインターフェイスにより解放される必要がある場合に機能します。

与えられたインターフェイスによるトークンの解放を可能とするため、unique_com_tokenトークンとインターフェイスポインタの両方を必要とし、インターフェイスへの強い参照を保持します。

例を見てください。DWORDトークンはIConnectionPointインターフェイスのメソッドにより取得および解放されます。

STDMETHOD(Advise)(IUnknown* sink, _Out_ DWORD* cookie) PURE;
STDMETHOD(Unadvise)(DWORD cookie) PURE;

このトークンにunique_com_tokenを使うには、最初にインターフェイストークンを受け取ってトークンを解放する関数を定義します。

inline void __stdcall IConnectionPointUnadvise(IConnectionPoint* source, DWORD cookie)
{
    source->Unadvise(cookie);
}

この関数はインターフェイストークンを管理するスマートポインタの定義に使います。

using unique_connection_point_cookie =
    unique_com_token<IConnectionPoint, DWORD, decltype(IConnectionPointUnadvise), IConnectionPointUnadvise>;

// インターフェイスのメソッドを直に渡すことも可能です。
// メンバーを呼び出すラッパー関数の記述を回避できます。
using unique_connection_point_cookie =
    unique_com_token<IConnectionPoint, DWORD, decltype(&IConnectionPoint::Unadvise), &IConnectionPoint::Unadvise>;

新しく定義した型を使用します。

wil::unique_connection_point_cookie cookie;

cookie.associate(connection_point);
THROW_IF_FAILED(connection_point->Advise(sink, &cookie));

使い方

基本的な使い方は以下のサンプルがカバーしています。意味論的にこの使い方はunique_anyとほぼ一致しており、所有するインターフェイスの管理が追加された程度です。

// 既存のトークンで新しいポインタを作成する。
wil::unique_connection_point_cookie registration(existing_source, existing_cookie);

// トークンを取得する。
auto cookie = registration.get();

// トークンが有効かチェックする。
if (registration)
{
    // トークンは割り当て済み。
}

// 現在のトークンを開放して新しいソースへの強い参照を得る。
// 注意:`&`演算子を使用する準備として機能します。
registration.associate(new_source);

// トークンとソースを解放する。
registration.reset();

// トークンを解放して置換する。ソースは保持される。
registration.reset(new_token);

// ソースとトークンを解放して置換する。
registration.reset(new_source, new_token);

// 解放せずにトークンをデタッチする。
// 注意:呼び出し側は既にソースへの参照を持っている必要がある。
auto resource = registration.release();

// 出力引数での使用のために内部トークンのアドレスを返す。
// 注意:呼び出し時に管理しているトークンは解放される。事前のソースの関連付けが必要。
WindowsApiCall(&resource);

// 上と同じ。
WindowsApiCall(resource.put());

// 入出力引数での使用のために内部トークンのアドレスを取得する。
// 注意:事前にソースの関連付けが必要。
WindowsApiCall(ptr.addressof());

// リソースを交換する。
registration.swap(registration2);

wil::unique_com_call

WILのunique_com_tokenトークン(またはハンドル)がCOMインターフェイスで解放できる場合にうまく機能します。WILのunique_com_callトークンがない場合に使用でき、操作の完了時にCOMインターフェイスのメソッドをただ呼び出せます。

与えられたインターフェイスの呼び出しを可能とするため、unique_com_callインターフェイスへの強い参照を保持します。

IClosableインターフェイスCloseメソッドの例をみてください。unique_com_callでこのメソッドを呼び出すには、最初にインターフェイスを受け取る関数を定義する必要があります。

inline void __stdcall CloseIClosable(IClosable* source)
{
    source->Close();
}

この関数はインターフェイスを管理するスマートポインタで使用します。

using unique_closable_call = unique_com_call<IClosable, decltype(CloseIClosable), CloseIClosable>;

// unique_com_tokenと同じ。
using unique_closable_call = unique_com_call<IClosable, decltype(&IClosable::Close), &IClosable::Close>;

新しく定義された型は次のように使えます。

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());
// ここにコードを記述します。
// 変数closeのデストラクタはIClosable::Closeを呼び出します。

使い方

基本的な使い方は以下のサンプルがカバーしています。意味論的に、この使い方はunique_anyとほぼ同じです。違いはこのクラスがインターフェイス自体と呼び出すメソッドを管理することです。COMポインタだけの点検やデタッチはできません。

// ソースインターフェイスを与えて新しい呼び出しを作成する。
wil::unique_closable_call call(closable.get())

// 呼び出しが未解決かチェックする。
if (call)
{
    // 呼び出しは作成済み。
}

// デストラクタの前にメソッドを呼び出す。インターフェイスも解放する。
call.reset();

// メソッドの呼び出しを解除する。インターフェイスも解放する。
call.release();

// 出力引数で使用するために内部COMインターフェイスのアドレスを返す。
// 注意:未解決の呼び出しが作成され、インターフェイスは解放されます。
WindowsApiCall(&call);

// 上と同じ。
WindowsApiCall(call.put());

// 入出力引数で使用するために内部COMインターフェイスのアドレスを返す。
// 呼び出しで保持されたインターフェイスはメモリリークします(?)。
WindowsApiCall(call.addressof());

// リソースを交換する。
call.swap(call2);

wil::unique_call

WILのunique_anyはグローバル関数で解放できるトークン(またはハンドル)がある場合にとても役立ちます。WILのunique_callトークンがない場合に使用でき、操作完了時にただグローバル関数を呼び出します。

CoUninitializeメソッドの例です。unique_callを呼び出しに使うために以下のようなスマートポインタ型を定義できます。

using unique_couninitialize_call = unique_call<decltype(&::CoUninitialize), ::CoUninitialize>;

スタック上に型インスタンスを作成するだけで型のスコープから出るときの呼び出しを保証できます。

RETURN_IF_FAILED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED));
unique_couninitialize_call cleanup;
// ここにコードを記述します。
// 早期にreturnされてもCoUninitializeが呼び出されます。

一度スマートポインタ型を設定すると、その呼び出しを要求する操作の完了時にその型を返す便利なヘルパーを定義できます。

_Check_return_ inline unique_couninitialize_call CoInitializeEx(DWORD coinitFlags = COINIT_MULTITHREADED)
{
    THROW_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags));
    return unique_couninitialize_call();
}

このヘルパーは次のパターンで簡単に呼び出せます。

auto cleanup = wil::CoInitializeEx();
// ここにコードを記述します。
// 早期にthrowやreturnされてもCoUninitializeが呼び出されます。

使い方

基本的な使い方は以下のサンプルがカバーしています。意味論的に、この使い方はunique_anyとほぼ同じです。違いはこのクラスがリソースではなく呼び出しを管理し、get()が存在しないことです。

// 新しい呼び出しを作成する。
wil::unique_couninitialize_call call;

// 呼び出しが管理外かチェックする。
if (call)
{
    // 呼び出しは作成済み。
}

// デストラクタの前に呼び出しを実行する。二回目は呼び出せない。
call.reset();

// 呼び出しを無効化する。
call.release();

// リソースを交換する。
call.swap(call2);

有効なunique_call型とヘルパー

グローバルメソッドコントラクトはいくつかの領域で一般的です。これらのメソッドがパブリックあるいは複数のチームに渡って共有されるヘッダーに公開されている場合、WILのResource.hにその型を追加すべきです。いくつかのインクルードガードテクニックとネーミングパターンはここに記載されています

以下はWILリソースヘッダーのカバーするパブリックに定義された型とヘルパーです。必要に応じてWILのResource.hを検索してください。見つからない場合、追加を検討してください。

unique_coreverttoself_call

ヘルパー:wil::CoImpersonateClientwil::CoImpersonateClient_failfast

unique_couninitialize_call

ヘルパー:wil::CoInitializeExwil::CoInitializeEx_failfast

unique_rouninitialize_call

ヘルパー: wil::RoInitializewil::RoInitialize_failfast

wil::scope_exit

RAIIテクニックが実用的ではない独特あるいは一般的ではないクリーンアップを行う場合、注意深いハンドルの制御フロー(早期returnや例外の回避)を試すかわりにwil::scope_exitの使用を検討してください。

wil::scope_exitはスコープから出るときに与えられたラムダ式を実行するオブジェクトを返します。これは早期returnや例外のthrow時にラムダ式の実行を可能とします。

例:

void MemberFunction()
{
    StartMemberFunction();
    auto cleanup = wil::scope_exit([&]
    {
        StopMemberFunction();
    });

    // ここにコードを記述します。
    // (おそらく早期returnや例外が発生します)

    // cleanupオブジェクトのデストラクタはStopMemberFunctionを実行します。
}

wil::scope_exitはパワフルなツールですが、既存のRAIIクラスや以上のクラスのカスタム版で置換できる場合があります。scope_exitを使用する前に実行されるクリーンアップコードがそのコードサイトで独特かどうか検討してください。もしもそのコードがユニークではない(例えばリソースの解放にグローバルメソッドやCOMインターフェイスメソッドを呼び出す場合)は既存のRAIIクラスを探して(あるいはResource.hの有効なプリミティブから新規作成して)、scope_exitの代わりに使用してください。もしもそれが広範に適用可能であれば、Resource.hへの記述とプルリクエストの送信も検討してください!

使い方

基本的な使い方、単にラムダ式を定義してローカル変数に保持します。

auto cleanup = wil::scope_exit([&]
{
    // クリーンアップコードを記述します。
});

scope_exit変数がデストラクトされる前にラムダ式を実行するにはreset()関数を使用します。早期に実行された場合でもラムダ式は一度しか実行されないことに注意してください。例です。

StartMemberFunction();
auto cleanup = wil::scope_exit([&]
{
    StopMemberFunction();
});

// コード……

// 変数cleanupのデストラクタ呼び出しよりも前にラムダ式を実行する。
cleanup.reset();

// さらなるコード……

ラムダ式の実行を完全に回避するにはrerelase()メソッドを呼び出します。この動作はイベントが失敗したときだけクリーンアップを呼び出す場合に適切です。例です。

HRESULT Start()
{
    RETURN_IF_FAILED(GetStarted());
    auto cleanup = wil::scope_exit([&]
    {
        StopEarly();
    });

    // 失敗時に早期returnしたり例外を発生するようなコード

    // 成功終了――StopEarly呼び出しを回避
    cleanup.release();
    return S_OK;
}

ベストプラクティス

  • scope_exitインスタンスとクリーンアップするリソースを密接に関連付ける(リソースの直後に定義する)。リソースとscope_exitの宣言に距離を加えることはその間の望まない早期returnや例外の導入を招く。
  • 既定ではscope_exitラムダ式における例外の発生はファイルファストとなる。scope_exit_logを使うことで例外の発生時に終了せずログを記録できる。
  • 任意のscope_exitラムダ式で既定の参照キャプチャ[&]だけ使用する。by-valueキャプチャを使用する場合、ラムダ式の作成時にコピーコンストラクタが例外を発生する可能性がある。
  • scope_exitを使いすぎない。使用はまれであるべき。より強力な型指定RAIIクラスの使用が推奨される。

リソースヘルパー

wil::out_param

多くのスマートポインタクラスは内部ポインタアドレスへのアクセスを許可しており、&演算子オーバーロードするかaddressofのようなメソッドを持ちます。有効な場合はこれらのメソッドを使用します。

スマートポインタクラスが内部ポインタへの直接アクセスを提供していない場合(例えばwistd::unique_ptr)、out_paramメソッドを使うことで二段階初期化を回避できます。

// こうしないでください。
// 以前行われていた二段階作成……
ITEMIDLIST_ABSOLUTE idListRaw = nullptr;
RETURN_IF_FAILED(propStore.GetAsIDList(PKEY_PropApply_ItemIDList, &idListRaw));
wil::unique_cotaskmem_ptr<ITEMIDLIST_ABSOLUTE> idlist(idListRaw);

// 代わりにこうします。
// wil::out_paramを使えば一回で済みます。
wil::unique_cotaskmem_ptr<ITEMIDLIST_ABSOLUTE> idlist;
RETURN_IF_FAILED(propStore.GetAsIDList(PKEY_PropApply_ItemIDList, wil::out_param(idlist)));

このヘルパーは、結果のポインタを受け取って破棄時にリソースをスマートポインタへ渡すスタック上の一時オブジェクトの作成を回避します。以下の警告を参照してください。

たまにABIで使われる出力ポインタ型がAPIで割り当てられる実際の基底型と一致しない場合があります。この場合、out_param_ptrを使用してAPIの定義を満たす型を得ることができます。

wil::unique_cotaskmem_ptr<ITEMID_CHILD> idlistNew;
RETURN_IF_FAILED(ILCloneWithHiddenID(childIdWithHidden.get(), pidhid, wil::out_param_ptr<PIDLIST_RELATIVE*>(idlistNew)));

警告wil::out_paramwil::out_param_ptrを使う場合、同じ式でそのスマートポインタを参照しないでください。

// こうしないでください。
// out_paramとスマートポインタ自体は式中で同時使用できません。
if (SUCCEEDED(foo->Bar(wil::out_param(ptr))) && ptr->IsGood()) {}

これはC++が「完結式(full expression)」が終わるまで一時変数のデストラクトを遅らせる事実の結果です。これはwil::out_paramのデストラクタがスマートポインタのデータを移動させる前にptr->IsGood()が評価されることを意味します。回避策はこのステートメントを2つに分割することです。

// 使用前のout_paramによるptrの初期化を保証するためにステートメントを2つに分割する。
if (SUCCEEDED(foo->Bar(wil::out_param(ptr))))
{
    if (ptr->IsGood()) {}
}

wil::detach_to_opt_param

このヘルパーはスマートポインタクラスに含まれるリソースをオプションのポインタ引数へデタッチします。このルーチンの使用は簡単なnullptrテストと与えられたスマートポインタ型を適切にデタッチするルーチンの呼び出しを提供します。これにより、オプションの出力引数の簡単な1行デタッチが可能となります。

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

サポートされるスマートポインタはMicrosoft::WRL::ComPtrwil::com_ptrwil::unique_anywil::unique_structunique_ptrです。

特定リソースのラッパーとヘルパー(訳註:リンクは原文を参照)

使用時の問題

Identifier not found compilation error referencing pick_your_smart_pointer

Resource.hをインクルードするに使用するスマートポインタ型で必要なハンドル型とハンドル解放関数を提供するヘッダーを保証します。

使い方セクションのインクルードの順番を参照してください。

著作権表示

この記事は以下の著作物を使用しています。

Copyright (c) Microsoft Corporation. All rights reserved. https://github.com/microsoft/wil/blob/master/LICENSE