スレッドとマーシャリング (C++/CX)

ほとんどの場合、標準 C++ オブジェクトと同様に、Windows ランタイム クラスのインスタンスにはどのスレッドからでもアクセスできます。 このようなクラスを "アジャイル" と呼びます。 ただし、Windows に付属する一部の Windows ランタイム クラスは非アジャイルであり、標準 C++ オブジェクトではなく、COM オブジェクトと同様に使用する必要があります。 非アジャイル クラスを使用するために COM の専門家になる必要はありませんが、クラスのスレッド モデルとマーシャリング動作を考慮する必要があります。 この技術情報では、非アジャイル クラスのインスタンスを使用する必要があるまれなシナリオの背景について説明し、ガイダンスを示します。

スレッド モデルとマーシャリング動作

Windows ランタイム クラスは、適用される 2 つの属性が示すとおり、さまざまな方法で同時実行スレッド アクセスをサポートできます。

  • ThreadingModel 属性には、 ThreadingModel 列挙体によって定義された STA、MTA、Both のいずれかの値を指定できます。

  • MarshallingBehavior 属性には、 MarshallingType 列挙体によって定義された Agile、None、Standard のいずれかの値を指定できます。

ThreadingModel 属性では、クラスをアクティブにしたときに読み込む場所を指定します。クラスの読み込み先として、ユーザー インターフェイス スレッド (STA) コンテキストのみ、バックグラウンド スレッド (MTA) コンテキストのみ、またはオブジェクトを作成するスレッドのコンテキスト (Both) を指定できます。 属性値は MarshallingBehavior 、さまざまなスレッド コンテキストでのオブジェクトの動作を示します。ほとんどの場合、これらの値を詳しく理解する必要はありません。 Windows API が提供するクラスの約 90 % には、 ThreadingModel=Both と MarshallingTypeAgile があります。 これは、低レベルのスレッドの詳細を透過的および効率的に処理できることを意味します。 ref new を使用して "agile" クラスを作成する場合、メイン アプリケーションのスレッドまたは 1 つ以上のワーカー スレッドから、そのクラスのメソッドを呼び出せます。 つまり、Windows から提供されたかサード パーティから提供されたかに関係なく、コード内の任意の場所でアジャイル クラスを使用できます。 クラスのスレッド モデルやマーシャリング動作に関係する必要はありません。

Windows ランタイム コンポーネントの使用

ユニバーサル Windows プラットフォーム アプリを作成すると、アジャイルと非アジャイル両方のコンポーネントと対話できます。 非アジャイル コンポーネントとやり取りすると、次の警告が表示されることがあります。

非アジャイル クラス使用時のコンパイラの警告 C4451

さまざまな理由で、一部のクラスはアジャイルにすることができません。 ユーザー インターフェイス スレッドとバックグラウンド スレッドの両方から非アジャイル クラスのインスタンスにアクセスする場合は、実行時の動作が正しいことを注意深く確認する必要があります。 アプリ内のグローバル スコープで非アジャイルのランタイム クラスをインスタンス化する場合、またはそれ自体がアジャイルとマーク付けされている ref クラスのクラス メンバーとして非アジャイル型を宣言する場合、Microsoft C++ コンパイラは警告を発します。

非アジャイル クラスで扱いが最も簡単なのは、 ThreadingModel=Bothおよび MarshallingType=Standard が指定されているものです。 これらのクラスをアジャイルにするには、単に Agile<T> ヘルパー クラスを使用します。 次の例は、 Windows::Security::Credentials::UI::CredentialPickerOptions^型の非アジャイル オブジェクトの宣言、および結果として生成されるコンパイラの警告を示しています。

ref class MyOptions
    {
    public:
        property Windows::Security::Credentials::UI::CredentialPickerOptions^ Options

        {
            Windows::Security::Credentials::UI::CredentialPickerOptions^ get()
            {
                return _myOptions;
            }
        }
    private:
        Windows::Security::Credentials::UI::CredentialPickerOptions^ _myOptions;
    };

次の警告が出されます。

Warning 1 warning C4451: 'Platform::Agile<T>::_object' : Usage of ref class 'Windows::Security::Credentials::UI::CredentialPickerOptions' inside this context can lead to invalid marshaling of object across contexts. Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead

マーシャリング動作が「標準」のオブジェクトへの参照を (メンバー スコープまたはグローバル スコープで) 追加する場合、コンパイラは、Platform::Agile<T> で型をラップするように勧める警告を発行します。Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' insteadAgile<T> を使用すると、他のすべてのアジャイル クラスと同じようにクラスを使用できます。 次のような状況では、 Platform::Agile<T> を使用します。

  • 非アジャイル変数は、グローバル スコープで宣言されます。

  • 非アジャイル変数はクラス スコープで宣言されます。使用コードが、ポインターを忍び込ませる、つまり、正しいマーシャリングなしで別のアパートメントで使用する可能性があります。

このどちらの状況にも該当しない場合は、含んでいるクラスを非アジャイルとしてマークできます。 つまり、非アジャイル クラスでは非アジャイル オブジェクトだけを直接保持し、アジャイル クラスでは Platform::Agile<T> を使用して非アジャイル オブジェクトを保持する必要があります。

次の例は、警告を安全に無視できるように、 Agile<T> を使用する方法を示しています。

#include <agile.h>
ref class MyOptions
    {
    public:
        property Windows::Security::Credentials::UI::CredentialPickerOptions^ Options

        {
            Windows::Security::Credentials::UI::CredentialPickerOptions^ get()
            {
                return m_myOptions.Get();
            }
        }
    private:
        Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions^> m_myOptions;

    };

Agile を ref クラスの戻り値またはパラメーターとして渡すことはできません。 Agile<T>::Get() メソッドは、パブリック メソッドまたはプロパティでアプリケーション バイナリ インターフェイス (ABI) 間で渡せるオブジェクトへのハンドル (^) を返します。

マーシャリング動作が「None」に指定されている内部プロシージャ Windows ランタイム クラスへの参照を作成すると、コンパイラは警告 C4451 を発行しますが、Platform::Agile<T> の使用を検討することはお勧めしません。 コンパイラは、この警告を超えるヘルプを提供できます。従って、クラスを正しく使用して、コードがユーザー インターフェイス スレッドからのみ STA コンポーネントを呼び出すこと、およびバックグラウンド スレッドからのみ MTA コンポーネントを呼び出すことを確認するのはユーザーの責任になります。

アジャイル Windows ランタイム コンポーネントの作成

C++/CX で ref クラスを定義する場合、それは既定でアジャイルです。つまり、ThreadingModel=Both と MarshallingType=Agile が指定されています。 Windows ランタイム C++ テンプレート ライブラリを使用する場合は、FreeThreadedMarshaller を使用する FtmBase から派生することで、クラスをアジャイルにできます。 ThreadingModel=Both または ThreadingModel=MTA が指定されたクラスを作成する場合、クラスがスレッドセーフであることを確認します。

ref クラスのスレッド モデルとマーシャリングの動作を変更できます。 ただし、クラスを非アジャイルにする変更を加えた場合は、その変更に関連付けられた影響を理解する必要があります。

次の例は、Windows ランタイム クラス ライブラリのランタイム クラスに、MarshalingBehavior および ThreadingModel 属性を適用する方法を示しています。 アプリケーションが DLL を使用し、 ref new キーワードを使用して MySTAClass クラス オブジェクトをアクティベートする場合、オブジェクトはシングル スレッド アパートメントでアクティベートされ、マーシャリングをサポートしません。

using namespace Windows::Foundation::Metadata;
using namespace Platform;

[Threading(ThreadingModel::STA)]
[MarshalingBehavior(MarshalingType::None)]
public ref class MySTAClass
{
};

シールされていないクラスでは、マーシャリング属性とスレッド属性が設定されている必要があります。これにより、コンパイラは派生クラスでこれらの属性の値が同じであることを検証できます。 クラスでこれらの属性が明示的に設定されていない場合、コンパイラはエラーを生成し、コンパイルは失敗します。 次のいずれかの場合、シールされていないクラスから派生したクラスでコンパイラ エラーが生成されます。

  • ThreadingModel および MarshallingBehavior 属性が派生クラスで定義されていない。

  • 派生クラスの ThreadingModelMarshallingBehavior 属性の値が基底クラスの値と一致しない。

サードパーティの Windows ランタイム コンポーネントで必要なスレッドとマーシャリングの情報は、コンポーネントのアプリ マニフェスト登録情報で指定します。 すべての Windows ランタイム コンポーネントをアジャイルにすることをお勧めします。 これにより、クライアント コードはアプリケーション内の任意のスレッドからコンポーネントを呼び出すことができます。また、それらの呼び出しは、マーシャリングがない直接呼び出しであるため、呼び出しのパフォーマンスが向上します。 この方法でクラスを作成する場合、クラスを使用するために、クライアント コードで Platform::Agile<T> を使用する必要はありません。

関連項目

ThreadingModel
MarshallingBehavior