potisanのプログラミングメモ

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

WIL GitHub Wiki WinRT&COMラッパーの和訳・改変

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

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

WinRT&COMラッパー

Raymond Chen、2019/10/4、リビジョン9個

WILのWinRT&COMラッパーstdスマートポインタであるstd::unique_ptrに似た方法でCOMポインタを安全に使う手助けをします。

使い方

COMラッパーは正しいヘッダーファイルをインクルードすれば使用できます。

#include <wil/com.h>

wil::com_ptr_t

WILのリソースラッパーと同じように、COMラッパーはwil::com_ptr_t<T>テンプレートクラスに基づいて使用できます。このテンプレートクラスはWILのCOMラッパーの基本機能を提供します。このクラスを直接インスタンス化する必要はありません。代わりに以下で示すようなエイリアスのひとつを使用します。

template <typename T,
          typename err_policy = err_exception_policy>
class com_ptr_t;

テンプレート引数

  • T com_ptr_tの管理する基底型。普通はCOMインターフェイスの名前です(末尾の*は不要です)。TIAgileReferenceまたはIWeakReferenceの場合はラッパークラスの動作がわずかに異なることに注意してください。詳細は以下のcom_agile_refまたはcom_weak_refを参照してください。
  • err_policy エラーハンドルポリシー。以下のエイリアスを参照してください。

エイリアス

以下のエイリアスは共通のエラーハンドリングポリシーのために定義されています。

エイリアス ポリシー 概要
com_ptr<T> err_exception_policy 失敗時に例外を発生します。
com_ptr_nothrow<T> err_returncode_policy 失敗を示すHRESULTを返します。
com_ptr_failfast<T> err_failfast_policy 失敗時はフェイルファストします。

APIリファレンス

メンバー型

  • result エラーポリシーの提供する関数の戻り値。例外またはフェイルファストではvoid、リターンコードポリシーではHRESULTです。
  • element_type com_ptr_tの保持するテンプレート型T
  • pointer com_ptr_tの保持するテンプレート型Tへのポインタ。T*

コンストラク

  • com_ptr_t()(デフォルトコンストラクタ) 空のラッパーを作成します。

  • com_ptr_t(nullptr) 空のラッパーを作成します。

  • com_ptr_t(T* ptr) 生のポインタの指すCOMオブジェクトへの追加参照を作成します。ptrnullptrの場合、空のラッパーを作成します。

  • com_ptr_t(const com_ptr_t<...>& other)com_ptr_t(const Microsoft::WRL::ComPtr<...>& other)(コピーコンストラクタ) otherの指すCOMオブジェクトへの追加参照を作成します。otherが空の場合、空のラッパーを作成します。ソースの基底要素がターゲットと互換性があれば、otherはどんな種類のcom_ptr_tComPtrでも指定できます。エラーハンドリングポリシーは一致する必要がありません。

  • com_ptr_t(com_ptr_t<...>&& other)com_ptr_t(Microsoft::WRL::ComPtr<...>&& other)(ムーブコンストラクタ)

    他のラッパーから所有権を転送された新しいラッパーを作成します。ソースの基底要素がターゲットと互換性があれば、otherはどんな種類のcom_ptr_tComPtrでも指定できます。

デストラク

  • ~com_ptr_t ラップするCOMオブジェクトを解放します。

オブジェクト管理メソッド

  • T* get() const ラップするCOMオブジェクトを返します。ラッパーが空ならnullptrを返します。
  • void reset() 現在のCOMオブジェクトを解放してラッパーを空にします。
  • void reset(nullptr) 現在のCOMオブジェクトを解放してラッパーを空にします。

reset(T* ptr)は存在しないことに注意してください。代わりに割り当て操作を使います。

  • void attach(T* ptr) 現在のCOMオブジェクトを解放して、与えられた生ポインタの所有権を与えます。ptrnullptrの場合、ラッパーは空になります。
  • T* detach() 現在のCOMオブジェクトを返してラッパーを空にします。返された生ポインタがnullではない場合、呼び出し側は解放の責任を持ちます。
  • T** addressof() 現在のCOMオブジェクトを解放せずに内部ポインタのアドレスを返します。現在のCOMオブジェクトがメモリリークする可能性があるため、出力引数では使わないでください。出力引数ではアドレスを返す前に現在のCOMオブジェクトを開放する&演算子を使用します。
  • T* const* addressof() const 上のconst版です。返されたポインタは変更できません。
  • T** put() 現在のCOMオブジェクトを解放して内部ポインタのアドレスを返します。com_ptr_tを出力引数に渡す場合に使用します。
  • T** operator&() put()と同じです。
  • void** put_void() put()と同じですが、void**を返します。
  • IUnknown** put_unknown() put()と同じですが、IUnknown**を返します。
  • swap(com_ptr_t<T, ...>& other)swap(Microsoft::WRL::ComPtr<T>& other) 別のラッパーとポインタを交換します。ラッパーは同じCOMインターフェイスをラップすべきですが、エラーポリシーは異なっても構いません。ADL(Argument Dependent Lookup)用途の解放関数としても使用可能です。
  • swap(com_ptr_t<T, ...>&& other)swap(Microsoft::WRL::ComPtr<T>&& other)右辺値参照(rvalue reference) 別のラッパーとポインタを交換します。ラッパーは同じCOMインターフェイスをラップすべきですが、エラーポリシーは異なっても構いません。

COM型変換関数

querycopyの2つの変換関数ファミリーがあります。

queryメソッドはラッパーが空ではないことを要求します。ラッパーが空の場合、動作は未定義です(普通はnullポインタでクラッシュします)。

copyメソッドはラッパーが空でも構いません。空の場合は戻り値も空になります。

try_プリフィックスはその関数が失敗する可能性を意味します。失敗時、関数は空のラッパーを作り、falseを返します(適切な場合)。

querycopyメソッドは元のCOM型が目的のCOM型と等しいか変換可能な場合を最適化しています。この場合、コピー時にAddRefが呼び出され、QueryInterfaceは呼び出されません。

すべてのquerycopyメソッドはconstです。

直接 Try
com_ptr_t<U> query<U>() com_ptr_t<U> copy<U>() 以下のノート1、2を参照 com_ptr_t<U> try_query<U>() com_ptr_t<U> try_copy<U>() 以下のノート1、2を参照
result query_to(U** p) result copy_to(U** p) bool try_query_to(U** p) bool try_copy_to(U** p)
result query_to(REFIID riid, void** ppv) result copy_to(REFIID riid, void** ppv) bool try_query_to(REFIID riid, void** ppv) bool try_copy_to(REFIID riid, void** ppv)
エラーポリシーに従ってエラーを報告します。 失敗時は空のラッパーかfalseを返します。
  • ノート1: querycopy はリターンコードポリシーでは使えません。
  • ノート2:(try_)query 、 (try_)copy は元のラッパーと同じエラーポリシーのcom_ptr_t を返します。

例外ベースラッパーとフェイルファストベースラッパーの典型的な使い方です。

// 以下のcom_ptrはcom_ptr_failfastと置き換えられます。

wil::com_ptr<IBaz> TryGetInnerBaz()
{
    wil::com_ptr<IBar> bar = ...;

    // barは空ではない必要があります。
    auto foo = bar.query<IFoo>();

    auto baz = foo->TryGetBaz();

    return baz;
}

// 上は以下のように単純化できます。

wil::com_ptr<IBaz> TryGetInnerBaz()
{
    wil::com_ptr<IBar> bar = ...;

    // barは空ではない必要があります。
    return bar.query<IFoo>()->TryGetBaz();
}

void Example()
{
    wil::com_ptr<IBar> bar = ...;
    auto foo = bar.try_query<IFoo>();
    if (foo)
    {
        foo->Method();
    }
}

リターンコードベースラッパーの典型的な使い方です。

HRESULT TryGetInnerBaz(_COM_Outptr_ IBaz** result)
{
    *result = nullptr;

    wil::com_ptr_nothrow<IBar> bar = ...;

    // barは空ではない必要があります。
    wil::com_ptr_nothrow<IFoo> foo;
    RETURN_IF_FAILED(bar.query_to(&foo));

    wil::com_ptr_nothrow<IBaz> baz;
    RETURN_IF_FAILED(foo->TryGetBaz(&baz));

    // bazは空かもしれないのでcopy_toを使います。
    RETURN_IF_FAILED(baz.copy_to(result));

    return S_OK;
}

HRESULT Example()
{
    wil::com_ptr_nothrow<IBar> bar = ...;
    auto foo = bar.try_query<IFoo>();
    if (foo)
    {
        foo->Method();
    }

    wil::com_ptr_nothrow<IBaz> baz;
    RETURN_IF_FAILED(bar.try_query_to(&baz));
    baz->Method();

    return S_OK;   
}

演算子

  • operator=(nullptr) 現在のCOMオブジェクトを解放してラッパーを空にします。
  • operator=(T* ptr) 現在のCOMオブジェクトを解放して、生のポインタの指すCOMオブジェクトへの追加参照を作成します。
  • operator=(const com_ptr_t<...>& other)& operator=(const Microsoft::WRL::ComPtr<...>& other) 現在のCOMオブジェクトを解放して、生のポインタの指すCOMオブジェクトへの追加参照を作成します。ソースの基底型がターゲットと互換性であればotherには任意の種類のcom_ptr_tComPtrを指定できます。例えば、com_ptr_failfast<IStream>からcom_ptr_nothrow<IUnknown>を作成できます。これはIStream*IUnknown*へ変換できるためです。エラーハンドリングポリシーは一致する必要がありません。
  • operator=(com_ptr_t<...>&& other)& operator=(Microsoft::WRL::ComPtr<...>&& other)(ムーブ代入) 現在のCOMオブジェクトを解放してotherから所有権を転送します。ソースの基底型がターゲットと互換性であればotherには任意の種類のcom_ptr_tComPtrを指定できます。
  • T** operator&() 現在のCOMオブジェクトを解放して内部ポインタのアドレスを返します。内部ポインタのアドレスを取得するだけでCOMオブジェクトを解放しない場合(まれな入出力引数で使用する場合)はaddressofメソッドを使用します。
  • operator bool() 現在のCOMオブジェクトが存在すればtrueを返します。空の場合はfalseを返します。
  • T* operator->() const ラップするCOMオブジェクトのポインタを返します。ラッパーが空の場合はクラッシュを起こします。
  • T& operator*() const ラップするCOMオブジェクトを参照解除します。ラッパーが空の場合はクラッシュを起こします。
  • ==, !=, <, <=, >, >= 比較演算子 これらは基底生ポインタの比較を実行します。左辺と右辺は基底ポインタの変換可能だけが要求され、エラーポリシーは異なってもかまいません。例えば、com_ptr<IUnknown>ComPtr<IStream>を比較できます。生ポインタとnullptrの一致または不一致も比較できます。

wil::com_ptr_t vs Microsoft::WRL::ComPtr

wil::com_ptr_tは以下のように簡潔な使用を可能とする例外とフェイルファストをサポートしています。

someObj.query<ISomeInterface>()->CallMethod();

ComPtrはエラーコードのみサポートします。例外の使用やフェイルファスト動作を求めるなら選択肢は明白です。例外へ変換するならwil::com_ptrを使います。HRESULTベースで既にWRL ComPtrを使うコードを使用する場合、ComPtrを使い続けられます。HRESULTベースで新しいコードを書く場合、wil::com_ptr_nothrowを使うことが推奨されます。

com_ptr様動作の独立関数

  • T* com_raw_ptr(x) 生のポインタを抽出します。xは生のポインタ、com_ptr<T>WRL::ComPtr<T>C++/CX T^ハットポインタのいずれかを指定できます(C++/CXハットポインタでは生のポインタは常にIInspectable*です)。
直接 Try
com_ptr<U> com_query<U>(x) com_ptr_failfast<U> com_query_failfast<U>(x)(例外なし版) com_ptr<U> try_com_query<U>(x) com_ptr_failfast<U> try_com_query_failfast<U>(x) com_ptr_nothrow<U> try_com_query_nothrow<U>(x)
com_ptr<U> com_copy<U>(x) com_ptr_failfast<U> com_copy_failfast<U>(x)(例外なし版) com_ptr<U> try_com_copy<U>(x) com_ptr_failfast<U> try_com_copy_failfast<U>(x) com_ptr_nothrow<U> try_com_copy_nothrow<U>(x)
void com_query_to(x, U** p) void com_query_to_failfast(x, U** p) HRESULT com_query_to_nothrow(x, U** p) bool try_com_query_to(x, U** p) bool try_com_query_to_failfast(x, U** p) bool try_com_query_to_nothrow(x, U** p)
void com_copy_to(x, U** p) void com_copy_to_failfast(x, U** p) HRESULT com_copy_to_nothrow(x, U** p) bool try_com_copy_to(x, U** p) (no fastfail version) (例外なし版)
void com_query_to(x, REFIID riid, void** ppv) void com_query_to_failfast(x, REFIID riid, void** ppv) HRESULT com_query_to_nothrow(x, REFIID riid, void** ppv) bool try_com_query_to(x, REFIID riid, void** ppv) bool try_com_query_to_failfast(x, REFIID riid, void** ppv) bool try_com_query_to_nothrow(x, REFIID riid, void** ppv)
void com_copy_to(x, REFIID riid, void** ppv) void com_copy_to_failfast(x, REFIID riid, void** ppv) HRESULT com_copy_to_nothrow(x, REFIID riid, void** ppv) bool try_com_copy_to(x, REFIID riid, void** ppv)(フェイルファストなし版)(例外なし版)

querycomメソッドにも独立関数版があります。これらはcom_raw_ptrが最初の引数で受け取る引数を受け取り、対応するquerycopyメソッドと同じ動作をします。これらの関数は、生ポインタでそのままCOM操作を実行したい場合に便利です。

C++/CXヘルパー

  • Object^ wil::cx_object_from_abi(x) com_raw_ptrの受け取る任意の引数を受け取り、IInspectable*Object^の順に変換します。
  • U^ wil::cx_safe_cast<U>(x) com_raw_ptrの受け取る任意の引数を受け取り、IInspectable*Object^の順に変換してからU^safe_castします。
  • U^ wil::cx_dynamic_cast<U>(x) com_raw_ptrの受け取る任意の引数を受け取り、IInspectable*Object^の順に変換してからU^dynamic_castします。

wil::com_agile_ref

wil::com_agile_refファミリクラスはcom_ptr<IAgileReference>エイリアスですが、以下の点が異なります。

クラス 等しい 説明
com_agile_ref com_ptr<IAgileReference> エラー時に例外を発生するIAgileReferenceラッパー。
com_agile_ref_failfast com_ptr_failfast<IAgileReference> エラー時にファイルファストするIAgileReferenceラッパー。
com_agile_ref_nothrow com_ptr_nothrow<IAgileReference> エラーコードを返すIAgileReferenceラッパー。

com_agile_refラッパークラスは普通のcom_ptrquerycopyメソッドの動作が異なります。これらはアジャイル参照そのものではなく、アジャイル参照のターゲットに動作します。言い換えれば、IUnknoqn::QueryInterfaceではなくIAgileReference::Resolveを使用します。querycopy動作はすべて解決動作となり、普通のcom_ptrのようなショートサーキットではありません。

com_agile_refオブジェクトで使用される追加の解放関数ヘルパー:

クエリファミリ コピーファミリ 説明
com_agile_ref com_agile_query(anything) com_agile_ref com_agile_copy(anything) 例外を発生するcom_agile_refを返します。
com_agile_ref_failfast com_agile_query_failfast(anything) com_agile_ref_failfast com_agile_copy_failfast(anything) フェイルファストするcom_agile_ref_failfastを返します。
HRESULT com_agile_query_nothrow(anything, IAgileReference** result) HRESULT com_agile_copy_nothrow(anything, IAgileReference** result) エラーコードベースのcom_agile_ref_nothrowを返します。
anythingはnullポインタや空のラッパー以外です。 anythingはnullポインタや空のラッパーでも可能です。

anything引数はcom_raw_ptrの受け取る任意の引数であり、このアジャイル参照はanythingの指すCOMインターフェイスをラップします。

コピー関数にはnullポインタや空のラッパーを渡すことが可能で、その場合は空のアジャイル参照を返して関数は成功するか(com_agile_copycom_agile_copy_failfast)、nullポインタを返して関数は成功します(com_agile_copy_nothrow)。

上のすべての関数はAgileReferenceOptions最終引数を受け取ります。省略時の既定値はAGILEREFERENCE_DEFAULTです。

以下は最後の独立関数です。

  • bool is_agile(anything) IAgileObjectをクエリすることでオブジェクトがアジャイルか判断します。anythingcom_raw_ptrが受け取る任意の引数で、nullポインタや空のラッパー以外です。

wil::com_weak_ref

wil::com_weak_refファミリのクラスはcom_ptr<IWeakReference>エイリアスですが、以下の点が異なります。

クラス 等しい 説明
com_weak_ref com_ptr<IWeakReference> エラー時に例外を発生するIWeakReferenceラッパー。
com_weak_ref_failfast com_ptr_failfast<IWeakReference> エラー時にフェイルファストするIWeakReferenceラッパー。rror.
com_weak_ref_nothrow com_ptr_nothrow<IWeakReference> エラーコードを返すIWeakReferenceラッパー。

com_weak_refラッパークラスは普通のcom_ptrquerycopyメソッドの動作が異なります。これらは弱い参照そのものではなく弱い参照のターゲットに動作します。言い換えれば、IUnknown::QueryInterfaceではなくIWeakReference::Resolveを呼び出します。すべてのquerycopy動作は解決動作となり、普通のcom_ptrのようなショートサーキットではありません。

弱い参照のターゲットが破壊済みの場合、動作はE_NOT_SETで失敗します。これはCOM自体が報告する失敗、RPC_E_DISCONNECTEDとは異なることに注意してください。

弱い参照を取得するオブジェクトはIInspectableを実装しているべきです。

com_weak_refオブジェクトで使用される追加の解放関数ヘルパー:

クエリファミリ コピーファミリ 説明
com_weak_ref com_weak_query(anything) com_weak_ref com_weak_copy(anything) 例外を発生するcom_weak_refを返します。
com_weak_ref_failfast com_weak_query_failfast(anything) com_weak_ref_failfast com_weak_copy_failfast(anything) フェイルファストするcom_weak_ref_failfast`を返します。
HRESULT com_weak_query_nothrow(anything, IAgileReference** result) HRESULT com_weak_copy_nothrow(anything, IAgileReference** result) エラーコードベースのcom_weak_ref_nothrow`を返します。
anythingはnullポインタや空のラッパー以外です。 anythingはnullポインタや空のラッパーでも可能です。

anything引数はcom_raw_ptrの受け取る任意の引数です。

コピー関数の場合、

コピー関数にはnullポインタや空のラッパーを渡すことが可能で、その場合は空のアジャイル参照を返して関数は成功するか(com_weak_copycom_weak_copy_failfast)、nullポインタを返して関数は成功します(com_weak_copy_nothrow)。

オブジェクト作成ヘルパー

オブジェクト作成ヘルパーは次のパターンに従います。

  • com_ptr<Interface> CoCreateInstance<Class, Interface>(DWORD dwClsContext)
  • com_ptr<Interface> CoCreateInstance<Interface>(REFCLSID rclsid, DWORD dwClsContext)
  • com_ptr<Interface> CoGetClassObject<Class, Interface>(DWORD dwClsContext)
  • com_ptr<Interface> CoGetClassObject<Interface>(REFCLSID rclsid, DWORD dwClsContext)

ノート:

  • You can specify a Class (which must support __uuidof) as a template parameter or a REFCLSID as a function parameter.
  • テンプレート引数Class__uuidofをサポートしているべきです)か関数引数REFCLSIDが使用できます。
  • テンプレート引数Interfaceは省略可能です。既定値はIUnknownです。
  • 関数引数dwClsContextは省略可能です。既定値はCLSCTX_INPROC_SERVERです。

以下の表はすべてのエラーポリシーを示します。

Error policy Functions
err_exception_policy 失敗時は例外発生。 com_ptr<Interface> CoCreateInstance(...) com_ptr<Interface> CoGetClassObject(...)
err_failfast_policy 失敗時はフェイルファスト。 com_ptr_failfast<Interface> CoCreateInstanceFailFast(...) com_ptr_failfast<Interface> CoGetClassObjectFailFast(...)
err_returncode_policy 失敗時は空を返す。 com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(...) com_ptr_nothrow<Interface> CoGetClassObjectNoThrow(...)

ストリームヘルパー

引数は以下に従います。

  • ISequentialStream* stream、null以外。
  • void* destsizeバイトの大きさ。
  • const void* sourcesizeバイトの大きさ。
  • unsigned long sizedestと`sourceバッファーのバイトサイズ。
  • long long distance、ストリーム中の相対位置。
  • unsigned long long position、ストリーム中の絶対位置(ストリームの開始からの相対位置)。
  • unsigned long origindistanceの解釈を決める STREAM_SEEK.aspx) 値。
  • ISequentialStream* sourceStream &ISequentialStream* targetStream、ストリームのコピーメソッドで使用される。
  • unsigned long long amount、コピーするバイト数。

すべてのヘルパー関数はCOMストリームに基づく単一の呼び出しをラップします。読み取り、書き込み、コピー操作が部分的な結果を返す場合、関数は保持するデータに対するそれら動作を内部でループしません。

_nothrow版は例外発生版と同じですが、HRESULTを返すこと、例外発生版の戻り値は最終引数に出力引数として明示することが異なります。

例外発生版 非例外発生版 ノート
void something(...) HRESULT something(...)
ReturnType something(...) HRESULT something_nothrow(..., ReturnType* result)
HRESULT something_nothrow(..., ReturnType* result = nullptr) 最終引数は省略可能。
関数 説明
unsigned long stream_read_partial(stream, dest, size)
HRESULT stream_read_partial_nothrow(stream, dest, size, unsigned long* result)
void stream_read(stream, dest, size)
HRESULT stream_read_nothrow(stream, dest, size)
void stream_read(stream, T* dest)
HRESULT stream_read_nothrow(stream, T* dest)
void stream_write(stream, source, size)
HRESULT stream_write_nothrow(stream, source, size)
void stream_write(stream, const T* source)
HRESULT stream_write_nothrow(stream, const T* source)
unsigned long long stream_size(stream)
HRESULT stream_size_nothrow(stream, unsigned long long* result)
unsigned long long stream_seek(stream, distance, origin)
HRESULT stream_seek_nothrow(stream, distance, origin, unsigned long long* result = nullptr)
unsigned long long stream_set_position(stream, position)
HRESULT stream_set_position_nothrow(stream, position, unsigned long long* result = nullptr)
unsigned long long stream_seek_from_current_position(stream, distance)
HRESULT stream_seek_from_current_position_nothrow(stream, distance, unsigned long long* result = nullptr)
unsigned long long stream_get_position(stream)
HRESULT stream_get_position_nothrow(stream, unsigned long long* result)
void stream_reset(stream)
HRESULT stream_reset_nothrow(stream)
unsigned long long stream_copy_bytes(sourceStream, targetStream, amount)
HRESULT stream_copy_bytes_nothrow(sourceStream, targetStream, amount, unsigned long long* result = nullptr)
unsigned long long stream_copy_all(sourceStream, targetStream)
HRESULT stream_copy_all_nothrow(sourceStream, targetStream, unsigned long long* result = nullptr)
void stream_copy_exact(sourceStream, targetStream, amount)
HRESULT stream_copy_exact_nothrow(sourceStream, targetStream, amount)

ノート1:転送されたバイト数が要求されたバイト数と異なる場合、HRESULT_FROM_WIN32(ERROR_INVALID_DATA)で失敗します。この失敗コードは不完全な書き込みでも発生して、書き込まれたデータに以上がなくても発生して誤解を招きます。問題はストリームがいっぱいであることです。

文字列ストリームヘルパー

戻り値 関数
void stream_write_string(ISequentialStream* stream, const wchar_t sourceString, size_t characterCount)
HRESULT stream_write_string_nothrow(ISequentialStream* stream, const wchar_t* sourceString, size_t characterCount)
unique_cotaskmem_string stream_read_string(ISequentialStream* stream, options = returns_empty)
HRESULT stream_read_string_nothrow(ISequentialStream* stream, PWSTR* result, options = returns_empty)

stream_write_string関数は数えた文字列をストリームへ書き込みます。書き込みフォーマットは多くが16ビット値であることから16ビット文字です。null終端は追加されません。これはIStream_WriteStr関数で使われるのと同じ形式です。

characterCountは典型的にwcslen(sourceString)ですが、文字列の一部を書き込む場合はそれより小さくても構いません。

characterCountが65,535を超えた場合、_nothrow版であってもフェイルファスト例外が発生します。

stream_read_string関数はstream_write_stringで書き込まれた文字列を読み込みます。返された文字列はCoTaskMemFreeで開放する必要があります。

オプションのoptions引数は長さ0の文字列を返すかどうかを制御します。

オプション 説明
wil::empty_string_options::returns_empty (既定) 長さ0の文字列は空の文字列を割り当てて返されます。
wil::empty_string_options::returns_null 長さ0の文字列はnullptrとして返されます。

stream_position_saver

stream_position_saverクラスは作成時にオプションストリームの位置(position)を保持して、破壊時にそれを復元します。

stream_position_saverは位置を保持していない場合、空として扱われます。空のstream_position_saverは破壊時に何もしません。

使用例:

// エラー時、ストリームの位置は開始時の位置に復元されます。
// 他の人もストリームから読み込めます。
auto saver = wil::stream_position_saver(stream);

auto header = wil::stream_read<MY_HEADER>(stream);
if (header.signature != MY_HEADER_SIGNATURE)
{
    // ストリームが拒否された。「saver」はポジションを復元する。
    THROW_HR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
}

// ストリームの処理が成功した。
// ストリームの位置の復元を取り消す。
saver.dismiss(); 

stream_position_saverはムーブ可能、コピー不可能です。stream_position_saverの移動はムーブ先のオブジェクトへストリームの位置の復元義務を転送します。ムーブ元のオブジェクトは空になります。

コンストラク

  • stream_position_saver(IStream* stream) 将来的にふくげんするストリームの位置をキャプチャします。ストリームの位置をキャプチャできない場合は例外を発生します。streamnullptrも可能であり、その場合は位置をキャプチャしません。

デストラク

  • ~stream_position_saver 空ではない場合は保存していたストリームの位置を復元します。復元に失敗した場合、エラーをログして実行を継続します。

メソッド

  • void update() 保存した位置をストリームの現在の位置に更新します。位置が取得できない場合は例外を発生します。空の場合はクラッシュします。
  • unsigned long long position() キャプチャした位置を返します。空の場合は無意味な値(garbage value)を返します。
  • void reset() ストリームの位置を復元します。復元失敗時は例外を発生します。stream_position_saverはこの位置を保持して、破壊時に再度位置を復元します。
  • void reset(IStream* newStream) ストリームの位置を復元して、将来的な復元のためにnewStreamの現在位置をキャプチャします。位置の復元または新しい位置のキャプチャに失敗した場合は例外を発生します(現在の実装にはバグがあります。新しいストリームの位置を取得できない場合、ストリームの復元位置として元のストリームからキャプチャしていた位置が設定されます)。newStreamnullptr以外です。
  • void dismiss() 保存したストリームの位置復元をキャンセルして、stream_position_saverを空にします。

サイトヘルパー

wil::unique_set_site_null_call

`wil::unique_set_site_null_callは破壊時にIObjectWithSite::SetSite(nullptr)を呼び出すRAII型です。これはunique_com_callパターンに従います。詳細はunique_com_callを参照してください。

このヘルパーは普通は明示的に作成されません。むしろサイトの設定、操作の実行、クリアのためにcom_set_siteで返されます。

明示的な使い方

// デモ目的限定。このパターンはcom_set_siteメソッドでカプセル化されます。
// com_set_siteは以下の3行を手書きする代わりに使えます。

auto objectWithSite = someObject.query<IObjectWithSite>();
objectWithSite->SetSite(someSite);
auto cleanup = wil::unique_set_site_null_call(objectWithSite.get());

someObject->Execute();

// スコープから出るときにクリーンアップされます。サイトはnullptrが設定されます。

wil::com_set_site

wil::com_set_siteヘルパーメソッドはオプションサイトにIObjectWithSiteをオプションで実装するオプションオブジェクトを設定します。これは破壊時にサイトにnullを設定するunique_set_site_null_callを返します。以下はunique_set_site_null_callの典型的な使い方です。

Gotchacom_set_siteの結果の保存を忘れた場合、サイトは即座にリセットされて、com_set_siteが何もしていないように見えます。

unique_set_site_null_call com_set_site(_In_opt_ IUnknown* obj, _In_opt_ IUnknown* site);

objがnullかIObjectWithSiteを実装しない場合、空のunique_set_site_null_callが返され、何も起きません。

objIObjectWithSiteを実装する場合、そのサイトはsiteに設定され、返されたunique_set_site_ull_callは破壊時にobjのサイトをnullに設定します。

使用例:

auto obj = ...;
auto cleanup = wil::com_set_site(obj.get(), site.get());

someObject->Execute();

// スコープから出るとき、サイトはnullptrに設定されます。

サイトの設定が目的で復元が不要な場合、以下のようにします。

auto obj = ...;
wil::com_set_site(obj.get(), site.get()).release();

wil::for_each_site

for_each_site関数はサイトチェインの各サイトにコールバックを実行します。すべてのエラーは無視され、サイトチェインウォークは終了されます。これは典型的にデバッグで使用されます。

template<typename TLambda>
void for_each_site(_In_opt_ IUnknown* obj, TLambda&& callback);

使用例:

void OutputDebugSiteChainWatchWindowText(IUnknown* site)
{
    OutputDebugStringW(L"これらのエントリをVisual Studio ウォッチウィンドウへコピペします。\n");
    wil::for_each_site(site, [](IUnknown* site)
    {
        wchar_t msg[64];
        StringCchPrintfW(msg, ARRAYSIZE(msg), L"((IUnknown*)0x%p)->__vfptr[0]\n", site);
        OutputDebugStringW(msg);
    });
}

内部処理

querycopyメソッドファミリーの実装は「クエリポリシー」オブジェクトに依存しています。クエリポリシーは次の2メソッドをサポートすべきです。

  • static HRESULT query(T* ptr, REFIID riid, void** result)
  • static HRESULT query(T* ptr, TResult** result)

これらのメソッドはある型のポインタを別の型のポインタへ変換します。ptrnullptr以外です。

最初のオーバーロードでは、対象インターフェイスインターフェイスIDにより実行時に特定されます。次のオーバーロードでは、対象インターフェイスは関数テンプレート引数によりコンパイル時に特定されます。これらの実装は最適化に長所があります。例えば、default_query_policyは与えられたポインタが戻り値と互換するか調査します。これにより、QueryInterfaceの呼び出しを回避してAddRefを呼び出すことができます。

次の3つのクエリポリシーが現在実装されています。

  • wil::details::agile_query_policy for IAgileReference
  • wil::details::weak_query_policy for IWeakReference
  • wil::details::default_query_policy for その他すべてのCOMインターフェイス

com_ptr_tは特定のインターフェイスwil::details::query_policy_helperクラステンプレートの特殊化により上記の型を使用します。手動で特定することはできません(例:com_ptr_t型のテンプレート引数を介して)。

加えて、これらの多様なquerycopyメソッドは部分的なコンストラクタの強制選択のためのタグ値を使用します。

タグ 目的 何が起こるか
wil::details::tag_com_query queryの作成 queryの失敗時は例外発生またはフェイルファストします(リターンコードポリシーでは使いません)。
wil::details::tag_try_com_query try_queryの作成 queryの失敗時は空のラッパーを作成します。
wil::details::tag_com_copy copyの作成 copyの失敗時は例外発生またはフェイルファストします。ソースがnullの場合は空のラッパーを作成します。
wil::details::tag_try_com_copy try_copyの作成 copyの失敗またはソースがnull時は空のラッパーを作成します。

著作権表示

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

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