2022/9/19:2020/8/25版→2022/3/5版に修正しました(そしてミスをしました)。
MicrosoftがGitHubでMITライセンス公開しているWILのGitHub WikiからRAIIリソースラッパーの記事を和訳・改変したものです。素人による翻訳なので、誤訳や著作権上の問題などありましたらご連絡いただけますと幸いです。
一部のリンク切れは意図的なものです。原文をご参照ください。
- 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ハンドル型(
HANDLE
、HKEY
、PSECURITY_DESCRIPTOR
等)の管理。
- Opaqueハンドル型(
wil::unique_any_array_ptr
- 要素も管理すべき生の型の配列の管理(
DWORD
、HANDLE
、IFoo*
等)。
- 要素も管理すべき生の型の配列の管理(
wil::unique_struct
- ある形式のクリーンアップを要求する生構造体の管理(
VARIANT
、PROCESS_INFORMATION
等)。
- ある形式のクリーンアップを要求する生構造体の管理(
wistd::unique_ptr
- あらゆる種類の型指定アロケーションへのポインタの管理。
wil::unique_com_token
wil::unique_com_call
- 特定COMインターフェイスの呼び出し(
IClosable::Close
等)の管理。
- 特定COMインターフェイスの呼び出し(
wil::unique_call
- グローバルメソッドの呼び出し(
CoUninitialize
、CoRevertToSelf
等)の管理。
- グローバルメソッドの呼び出し(
wil::scope_exit
- ラムダ式として与えられた関数呼び出しの管理。
wil::unique_any
WILのunique_any
は任意のOpaqueリソース型の保持に特化したtraitsベースのスマートポインタです。std::unique_ptr
(呼び出し側により多様な特殊化を保持する)とは異なり、unique_any
はWindowsが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
テンプレート型はクリーンアップ関数がよく知られている場合に使用できます。
注意:型T
はtrivially 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[]>
メソッドはデフォルトコンストラクタを使用して要素数size
のT
の配列を割り当てます。
有効なunique_any
ハンドル型
WILのunique_any
スマートポインタは典型的なWindowsリソース型を含むことができます。例えばファイルハンドル(unique_hfile
)、同期プリミティブ(unique_event
、unique_semaphore
、unique_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_handle
はnullptr
を空状態とするカーネルハンドルに使われます。一方、unique_hfile
はINVALID_HANDLE_VALUE
を空状態とするファイルハンドルに使われます。- スレッドプールハンドル(
unique_threadpool_*
)について、サフィックスのない型名は解放をキャンセルして任意の未解決コールバックを完了まで待ちます。_nocancel
サフィックスは解放を待機します。_nowait
サフィックスはキャンセルも待機もしません。 unique_hlocal_sid
はunique_any_psid
のより慣用的な名前です。これはSIDをLocalFree
で解放して、必要ならLocalAlloc
で割り当てます。unique_event
はwil::slim_event
の軽量版です。詳細は後述します。unique_hdc
はDeleteDC
でDCを解放します。unique_hdc_window
はReleaseDC
でDCを解放します(ウィンドウハンドルはhwnd
メンバーが利用できます)。unique_hdc_paint
はEndPaint
でDCを解放します(ウィンドウハンドルとPAINTSTRUCT
はhwnd
とps
メンバーが利用できます)。unique_wdf_spin_lock
とunique_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_any
かunique_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_ptr
はstd::array
のインターフェイスをモデルとしているため、詳細はstd::unique_ptr
や std::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_INFORMATION
はCreateProcess
が返す生の構造体であり、使用完了時にプロセスとスレッドのハンドルを明示的に閉じることを呼び出し側に要求します。
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_information
はPROCESS_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_struct
はZeroMemory
により構造体を初期化します。もしも非0無効化状態が必要な場合(初期化時にcbSize
値が必要な場合等)はテンプレート引数にカスタム初期化関数を指定してください(カスタム解放メソッドとまったく同じ方法です)。
wil::unique_any
との主要な違いは次の通りです。
- スマートポインタ型は生の構造体から派生します。含むのではありません(is-a関係)。
- ABIコントラクトでは構造体の入出力が頻繁であるため、
&
演算子は内部リソースを解放しません。必要に応じてreset_and_addressof()
メソッドを使用してください。
使い方
基本的な使い方は以下のサンプルがカバーしています。unique_struct
はstd::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
STLのstd::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_nothrow
と 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);
使用例:
// 割り当て失敗時に「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::CoImpersonateClient
、wil::CoImpersonateClient_failfast
unique_couninitialize_call
ヘルパー:wil::CoInitializeEx
、wil::CoInitializeEx_failfast
unique_rouninitialize_call
ヘルパー: wil::RoInitialize
、wil::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_param
やwil::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::ComPtr
、wil::com_ptr
、wil::unique_any
、wil::unique_struct
、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
使用時の問題
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