C++/WinRT でのアジャイル オブジェクト
ほとんどの場合、Windows ランタイム クラスのインスタンスには、任意のスレッドからアクセスできます (ほとんどの標準 C++ オブジェクトと同様)。 このような Windows ランタイム クラスが "アジャイル" です。 Windows に組み込まれている Windows ランタイム クラスのうち少数はアジャイル以外ですが、それらを使用するときに、スレッド モデルおよびマーシャリング動作を考慮する必要があります (マーシャリングでは、アパートメント境界を越えてデータが渡されます)。 すべての Windows ランタイム オブジェクトにおいて、アジャイルであることは既定値として適切なため、ユーザー独自の C++/WinRT 型は既定でアジャイルになります。
ただし、オプトアウトできます。たとえば、特定のシングルスレッド アパートメントなど、特別な理由で特定の型のオブジェクトを存在させることが必要な場合があります。 これは通常、再入可能要件に関係します。 しかし、ユーザー インターフェイス (UI) API でもアジャイル オブジェクトが提供されることが増えています。 一般に、アジリティは最もシンプルで最もパフォーマンスの高いオプションです。 また、ライセンス認証 ファクトリを実装する場合、対応するランタイム クラスがアジャイルでない場合でも、ライセンス認証 ファクトリはアジャイルである必要があります。
注意
Windows ランタイム は COM に基づいています。 COM の用語では、アジャイル クラスは ThreadingModel
= Both に登録されています。 COM スレッド モデルとアパートメントの詳細については、「COM スレッド モデルの理解と使用」をご覧ください。
コード例
ランタイム クラスの実装例を使用して、C++/WinRT でアジリティがどのようにサポートされるか示しましょう。
#include <winrt/Windows.Foundation.h>
using namespace winrt;
using namespace Windows::Foundation;
struct MyType : winrt::implements<MyType, IStringable>
{
winrt::hstring ToString(){ ... }
};
オプトアウトしていないため、この実装はアジャイルです。 winrt::implements 基本構造体は IAgileObject と IMarshal を実装します。 IMarshal 実装は CoCreateFreeThreadedMarshaler を使用して、IAgileObject を認識しないレガシー コードに対して適切な処理を実行します。
このコードは、オブジェクトのアジリティをチェックします。 myimpl
がアジャイルでない場合、IUnknown::as を呼び出すと例外がスローされます。
winrt::com_ptr<MyType> myimpl{ winrt::make_self<MyType>() };
winrt::com_ptr<IAgileObject> iagileobject{ myimpl.as<IAgileObject>() };
例外を処理する代わりに、IUnknown::try_as を呼び出すことができます。
winrt::com_ptr<IAgileObject> iagileobject{ myimpl.try_as<IAgileObject>() };
if (iagileobject) { /* myimpl is agile. */ }
IAgileObject には独自のメソッドがないため、それを使用して多くのことを行うことはできません。 したがって、この次のバリアントはより典型的です。
if (myimpl.try_as<IAgileObject>()) { /* myimpl is agile. */ }
IAgileObject はマーカー インターフェイスです。 IAgileObject のクエリが成功するか失敗するかだけで、そこから得られる情報と有用性の程度が決まります。
アジャイル オブジェクト サポートのオプトアウト
winrt::non_agile マーカー構造体をテンプレート引数として基本クラスに渡すことで、アジャイル オブジェクトのサポートを明示的にオプトアウトすることを選択できます。
winrt::implements から直接派生する場合。
struct MyImplementation: implements<MyImplementation, IStringable, winrt::non_agile>
{
...
}
ランタイム クラスを作成している場合。
struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, winrt::non_agile>
{
...
}
可変個引数パラメーター パックのどこにマーカー構造体が現れるかは関係ありません。
アジリティをオプトアウトするかどうかに関係なく、IMarshal を自分で実装できます。 たとえば、marshal-by-value (値渡しのマーシャリング) セマンティクスをサポートするためなどに、winrt::non_agile マーカーを使用して既定のアジリティの実装を回避し、自分で IMarshal を実装できます。
アジャイル参照 (winrt::agile_ref)
アジャイルではないオブジェクトを使用しているが、それを潜在的にアジャイルなコンテキストで渡す必要がある場合、1 つのオプションは、winrt::agile_ref 構造体テンプレートを使用して、非アジャイル型のインスタンス、または非アジャイル オブジェクトのインターフェイスへのアジャイル参照を取得することです。
NonAgileType nonagile_obj;
winrt::agile_ref<NonAgileType> agile{ nonagile_obj };
または、winrt::make_agile ヘルパー関数を使用することもできます。
NonAgileType nonagile_obj;
auto agile{ winrt::make_agile(nonagile_obj) };
いずれの場合も、agile
を別のアパートメントのスレッドに自由に渡して、そこで使用できるようになります。
co_await resume_background();
NonAgileType nonagile_obj_again{ agile.get() };
winrt::hstring message{ nonagile_obj_again.Message() };
agile_ref::get 呼び出しは、get が呼び出されるスレッド コンテキスト内で安全に使用できるプロキシを返します。
重要な API
- IAgileObject インターフェイス
- IMarshal インターフェイス
- winrt::agile_ref 構造体テンプレート
- winrt::implements 構造体テンプレート
- winrt::make_agile 関数テンプレート
- winrt::non_agile マーカー構造体
- winrt::Windows::Foundation::IUnknown::as 関数
- winrt::Windows::Foundation::IUnknown::try_as 関数