OLE スレッド モデルの説明と動作

この記事では、OLE スレッド モデルについて説明します。

元の製品バージョン: OLE スレッド モデル
元の KB 番号: 150777

概要

COM オブジェクトは、プロセスの複数のスレッドで使用できます。 "シングル スレッド アパートメント" (STA) と "マルチスレッド アパートメント" (MTA) という用語は、オブジェクトとスレッド間のリレーションシップ、オブジェクト間のコンカレンシーリレーションシップ、オブジェクトにメソッド呼び出しを配信する手段、およびスレッド間でインターフェイス ポインターを渡すための規則を記述するための概念的なフレームワークを作成するために使用されます。 コンポーネントとそのクライアントは、COM で現在サポートされている次の 2 つのアパートメント モデルから選択します。

  1. シングルスレッド アパートメント モデル (STA): プロセス内の 1 つ以上のスレッドが COM を使用し、COM オブジェクトの呼び出しが COM によって同期されます。 インターフェイスはスレッド間でマーシャリングされます。 特定のプロセス内の 1 つのスレッドのみが COM を使用するシングル スレッド アパートメント モデルの退化したケースは、単一スレッド モデルと呼ばれます。 以前は、STA モデルを単に "アパートメント モデル" と呼ぶこともあります。

  2. マルチスレッド アパートメント モデル (MTA): 1 つ以上のスレッドが COM を使用し、MTA に関連付けられている COM オブジェクトへの呼び出しは、呼び出し元とオブジェクトの間にシステム コードを組み合わせることなく、MTA に関連付けられているすべてのスレッドによって直接行われます。 複数の同時クライアントが多かれ少なかれ同時にオブジェクトを呼び出している可能性があるため (マルチプロセッサ システムでは同時に)、オブジェクトは自身で内部状態を同期する必要があります。 インターフェイスはスレッド間でマーシャリングされません。 以前は、このモデルを "フリースレッド モデル" と呼ぶこともあります。

  3. STA モデルと MTA モデルの両方を同じプロセスで使用できます。 これは"混合モデル" プロセスと呼ばれることもあります。

MTA は NT 4.0 で導入され、Windows 95 と DCOM95 で使用できます。 STA モデルは、Windows NT 3.51 および Windows 95 のほか、DCOM95 を使用した NT 4.0 および Windows 95 にも存在します。

概要

COM のスレッド モデルは、異なるスレッド アーキテクチャを使用するコンポーネントが連携するメカニズムを提供します。 また、それらを必要とするコンポーネントに同期サービスを提供します。 たとえば、特定のオブジェクトは 1 つのスレッドによってのみ呼び出されるように設計されており、クライアントからの同時呼び出しを同期しない場合があります。 このようなオブジェクトが複数のスレッドによって同時に呼び出されると、クラッシュしたり、エラーが発生したりします。 COM には、スレッド アーキテクチャのこの相互運用性に対処するためのメカニズムが用意されています。

スレッド対応コンポーネントでも、多くの場合、同期サービスが必要です。 たとえば、OLE/ActiveX コントロール、インプレース アクティブ埋め込み、ActiveX ドキュメントなどのグラフィカル ユーザー インターフェイス (GUI) を持つコンポーネントには、COM 呼び出しとウィンドウ メッセージの同期とシリアル化が必要です。 COM は、複雑な同期コードを使用せずにこれらのコンポーネントを記述できるように、これらの同期サービスを提供します。

"アパートメント" には、いくつかの相互関連の側面があります。 1 つ目は、スレッドと COM オブジェクトのセットの関係など、コンカレンシーを考える論理コンストラクトです。 2 つ目は、COM 環境から予想されるコンカレンシー動作を受け取るためにプログラマが従う必要がある一連のルールです。 最後に、COM オブジェクトに関するスレッドコンカレンシーをプログラマが管理するのに役立つシステム提供のコードです。

"アパートメント" という用語は、プロセスが"アパートメント" と呼ばれる一連の関連する異なる "ロケール" に分割された "建物" など、個別のエンティティとして考えられるメタファーに由来します。アパートメントは、オブジェクトと場合によってはスレッド間の関連付けを作成する "論理コンテナー" です。 スレッドはアパートメントではありませんが、STA モデルのアパートメントに論理的に関連付けられているスレッドが 1 つある可能性があります。 オブジェクトはアパートメントではありませんが、すべてのオブジェクトは 1 つのアパートメントと 1 つのみ関連付けられます。 しかし、アパートメントは単なる論理構造だけではありません。そのルールは、COM システムの動作を記述します。 アパートメント モデルの規則に従わない場合、COM オブジェクトは正しく機能しません。

詳細情報

シングル スレッド アパートメント (STA) は、特定のスレッドに関連付けられた一連の COM オブジェクトです。 これらのオブジェクトは、スレッドによって作成されるか、より正確には、まずスレッド上の COM システムに (通常はマーシャリングによって) 公開されることによって、アパートメントに関連付けられます。 STA は、オブジェクトまたはプロキシが "存在する" 場所と見なされます。オブジェクトまたはプロキシに (同じプロセスまたは別のプロセスで) 別のアパートメントからアクセスする必要がある場合は、そのインターフェイス ポインターを、新しいプロキシが作成されるそのアパートメントにマーシャリングする必要があります。 アパートメント モデルの規則に従っている場合、同じプロセス内の他のスレッドからの直接呼び出しは、そのオブジェクトでは許可されません。特定のアパートメント内のすべてのオブジェクトが 1 つのスレッドで実行されるという規則に違反する可能性があります。 STA で実行されているほとんどのコードは、追加のスレッドで実行すると正常に機能しないため、ルールが存在します。

STA に関連付けられているスレッドは、着信呼び出しを受信するために、呼び出すかCoInitializeCoInitializeEx(NULL, COINIT_APARTMENTTHREADED)、関連付けられているオブジェクトのウィンドウ メッセージを取得してディスパッチする必要があります。 COM は、この記事の後半で説明するように、ウィンドウ メッセージを使用して STA 内のオブジェクトの呼び出しをディスパッチして同期します。

"main STA" は、特定のプロセス内で呼び出すか、最初CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)に呼び出CoInitializeすスレッドです。 この記事で後述するように、一部のインプロセス オブジェクトは常にメイン STA に読み込まれるため、すべての COM 作業が完了するまで、プロセスのメイン STA は存続している必要があります。

Windows NT 4.0 と DCOM95 では、マルチスレッド アパートメント (MTA) と呼ばれる新しいタイプのアパートメントが導入されています。 MTA は、プロセス内のスレッドのセットに関連付けられた一連の COM オブジェクトであり、すべてのスレッドがシステム コードの相互配置なしで任意のオブジェクト実装を直接呼び出すことができます。 MTA 内の任意のオブジェクトへのインターフェイス ポインターは、マーシャリングすることなく、MTA に関連付けられているスレッド間で渡される場合があります。 呼び出す CoInitializeEx(NULL, COINIT_MULTITHREADED) プロセス内のすべてのスレッドが、MTA に関連付けられています。 前述の STA とは異なり、MTA のスレッドは、関連するオブジェクトが着信呼び出しを受信するためにウィンドウ メッセージを取得してディスパッチする必要はありません。 COM は、MTA 内のオブジェクトへの呼び出しを同期しません。 MTA 内のオブジェクトは、複数の同時スレッドの相互作用によって内部状態を破損から保護する必要があります。また、異なるメソッド呼び出し間で定数が残っている Thread-Local Storage の内容について想定することはできません。

プロセスには任意の数の STA を含めることができますが、最大で 1 つの MTA を持つことができます。 MTA は 1 つ以上のスレッドで構成されます。 STA には、それぞれ 1 つのスレッドがあります。 スレッドは、最大で 1 つのアパートメントに属しています。 オブジェクトは 1 つだけのアパートメントに属しています。 インターフェイス ポインターは常にアパートメント間でマーシャリングする必要があります (ただし、マーシャリングの結果はプロキシではなく直接ポインターである可能性があります)。 以下の情報を CoCreateFreeThreadedMarshaler参照してください。

プロセスは、COM によって提供されるスレッド モデルの 1 つを選択します。 STA モデル プロセスには 1 つ以上の STA があり、MTA はありません。 MTA モデル プロセスには、1 つ以上のスレッドを持つ 1 つの MTA があり、STA はありません。 混合モデル プロセスには、1 つの MTA と任意の数の STA があります。

シングルスレッドのアパートメント モデル

COM ではウィンドウ メッセージを使用して、このモデル内のオブジェクトに対する呼び出しの配信を同期およびディスパッチするため、STA のスレッドはウィンドウ メッセージを呼び出CoInitializeCoInitializeEx(NULL, COINIT_APARTMENTTHREADED)すか、取得してディスパッチする必要があります。 詳細については、以下の「参照」セクションを参照してください。

STA モデルをサポートするサーバー:

STA モデルでは、ウィンドウに投稿されたウィンドウ メッセージが同期されるのと同じ方法で、オブジェクトへの呼び出しが COM によって同期されます。 呼び出しは、ウィンドウ メッセージを使用してオブジェクトを作成したスレッドに配信されます。 その結果、オブジェクトのスレッドが呼び出され、呼び出 Get/PeekMessageDispatchMessage を受信する必要があります。 COM では、各 STA に関連付けられた非表示ウィンドウが作成されます。 STA の外部からのオブジェクトへの呼び出しは、この非表示ウィンドウに投稿されたウィンドウ メッセージを使用して、COM ランタイムによってオブジェクトのスレッドに転送されます。 オブジェクトの STA に関連付けられたスレッドがメッセージを取得してディスパッチすると、非表示ウィンドウのウィンドウ プロシージャも COM によって実装され、メッセージを受け取ります。 COM ランタイムは COM 所有のスレッドから STA のスレッドへの呼び出しの両側にあるため、ウィンドウ プロシージャは、COM ランタイムによって STA に関連付けられているスレッドを "フック" するために使用されます。 COM ランタイム (現在は STA のスレッドで実行されています) は、COM 提供のスタブを介してオブジェクトの対応するインターフェイス メソッドに "up" を呼び出します。 メソッド呼び出しから返される実行パスは、"up" 呼び出しを反転します。この呼び出しはスタブと COM ランタイムに戻り、ウィンドウ メッセージを介して COM ランタイム スレッドに制御を戻し、COM チャネルを介して元の呼び出し元に戻ります。

複数のクライアントが STA オブジェクトを呼び出すと、STA で使用される制御メカニズムの転送によって、呼び出しがメッセージ キューに自動的にキューに入れられます。 オブジェクトは、STA がメッセージを取得してディスパッチするたびに呼び出しを受け取ります。 この方法で呼び出しは COM によって同期され、呼び出しは常にオブジェクトの STA に関連付けられた 1 つのスレッドで配信されるため、オブジェクトのインターフェイスの実装で同期を提供する必要はありません。

注:

インターフェイス メソッドの実装がメソッド呼び出しの処理中にメッセージを取得してディスパッチし、同じ STA によって別の呼び出しがオブジェクトに配信される場合、オブジェクトを再入力できます。 これが発生する一般的な方法は、STA オブジェクトが COM を使用してアウトアウト (クロスアパートメント/クロスプロセス) 呼び出しを行う場合です。 これは、メッセージの処理中にメッセージを取得してディスパッチする場合にウィンドウ プロシージャを再入力できる方法と同じです。 COM では、同じスレッドでの再入り込みは防止されませんが、同時実行は防止されます。 また、COM 関連の再入可能を管理できる手段も提供されます。 詳細については、以下の「参照」セクションを参照してください。 メソッドの実装がアパートメントから呼び出されない場合や、メッセージを取得してディスパッチしない場合、オブジェクトは再入力されません。

STA モデルにおけるクライアントの責任:

STA モデルを使用するプロセスまたはスレッドで実行されているクライアント コードは、アパートメント間でオブジェクトのインターフェイスをマーシャリングする必要があります。次を使用して、オブジェクトのインターフェイスを CoMarshalInterThreadInterfaceInStream マーシャリングする CoGetInterfaceAndReleaseStream必要があります。 たとえば、クライアントのアパートメント 1 にインターフェイス ポインターがあり、アパートメント 2 がそれを使用する必要がある場合、アパートメント 1 はインターフェイスをマーシャリングする CoMarshalInterThreadInterfaceInStream必要があります。 この関数によって返されるストリーム オブジェクトはスレッド セーフであり、そのインターフェイス ポインターは、Apartment 2 からアクセスできる直接メモリ変数に格納する必要があります。 アパートメント 2 は、このストリーム インターフェイスを渡して CoGetInterfaceAndReleaseStream 、基になるオブジェクト上のインターフェイスのアンマーシャリングを解除し、オブジェクトにアクセスできるプロキシへのポインターを取得する必要があります。

一部のインプロセス オブジェクトはメイン アパートメントに読み込まれるため、クライアントがすべての COM 作業を完了するまで、特定のプロセスのメイン アパートメントは存続している必要があります。 (詳細については、以下を参照してください)。

マルチスレッド アパートメント モデル

MTA は、プロセス内のすべてのスレッドによって作成または公開されたオブジェクトのコレクションで、呼び出 CoInitializeEx(NULL, COINIT_MULTITHREADED)されました。

注:

COM の現在の実装では、明示的に COM を初期化しないスレッドを MTA の一部にすることができます。 COM を初期化しないスレッドは、プロセス内の少なくとも 1 つの他のスレッドが以前に呼び出 CoInitializeEx(NULL, COINIT_MULTITHREADED)された後に COM の使用を開始した場合にのみ、MTA の一部です。 (クライアント スレッドが明示的に行っていないときに COM 自体が MTA を初期化している可能性もあります。たとえば、STA 呼び出し CoGetClassObject/CoCreateInstance[Ex] に関連付けられたスレッドなど)"ThreadingModel=Free" とマークされた CLSID 上で、COM によって、クラス オブジェクトが読み込まれる MTA が暗黙的に作成されます)。スレッド モデルの相互運用性に関する情報を以下に示します。

ただし、これは特定の状況でアクセス違反などの問題を引き起こす可能性がある構成です。 そのため、COM 作業を行う必要がある各スレッドは、COM を呼び出して初期化してから、COM 作業の完了時に呼び出 CoInitializeEx しを行 CoUninitializeうことをお勧めします。 MTA を "不必要に" 初期化するコストは最小限です。

COM ではこのモデルのウィンドウ メッセージを使用してオブジェクトへの呼び出しを配信しないため、MTA スレッドはメッセージを取得してディスパッチする必要はありません。

  • MTA モデルをサポートするサーバー:

    MTA モデルでは、オブジェクトへの呼び出しは COM によって同期されません。 複数のクライアントは、異なるスレッドでこのモデルをサポートするオブジェクトを同時に呼び出すことができます。また、オブジェクトは、イベント、ミューテックス、セマフォなどの同期オブジェクトを使用して、インターフェイス/メソッドの実装で同期を提供する必要があります。MTA オブジェクトは、オブジェクトのプロセスに属する COM で作成されたスレッドのプールを介して、複数のプロセス外クライアントから同時呼び出しを受け取ることができます。 MTA オブジェクトは、MTA に関連付けられている複数のスレッド上の複数のインプロセス クライアントから同時呼び出しを受信できます。

  • MTA モデルのクライアント責任:

    MTA モデルを使用するプロセスまたはスレッドで実行されているクライアント コードは、それ自体と他の MTA スレッドの間でオブジェクトのインターフェイス ポインターをマーシャリングする必要はありません。 代わりに、ある MTA スレッドは、別の MTA スレッドから取得したインターフェイス ポインターを直接メモリ ポインターとして使用できます。 クライアント スレッドがプロセス外オブジェクトを呼び出すと、呼び出しが完了するまで中断されます。 呼び出しは、MTA に関連付けられているオブジェクトに到着することがありますが、MTA に関連付けられているアプリケーションで作成されたすべてのスレッドは、外出中の呼び出しでブロックされます。 この場合、一般に、着信呼び出しは COM ランタイムによって提供されるスレッドで配信されます。 メッセージ フィルター (IMessageFilter) は、MTA モデルでは使用できません。

混合スレッド モデル

混合スレッド モデルをサポートするプロセスでは、1 つの MTA と 1 つ以上の STA が使用されます。 インターフェイス ポインターは、すべてのアパートメント間でマーシャリングする必要がありますが、MTA 内でマーシャリングせずに使用できます。 STA 内のオブジェクトへの呼び出しは COM によって同期され、1 つのスレッドでのみ実行されますが、MTA 内のオブジェクトへの呼び出しは実行されません。 ただし、通常、STA から MTA への呼び出しはシステム指定のコードを経由し、STA スレッドから MTA スレッドに切り替えてからオブジェクトに配信されます。

注:

直接ポインターを使用できるケース、および STA スレッドが最初に MTA に CoCreateFreeThreadedMarshaler() 関連付けられたオブジェクトに直接呼び出す方法、および複数のアパートメントから直接呼び出す方法については、以下の SDK ドキュメントとその API の説明を参照してください。

スレッド モデルの選択

コンポーネントは、混合スレッド モデルを使用して、STA モデル、MTA モデル、または 2 つの組み合わせをサポートすることを選択できます。 たとえば、広範な I/O を実行するオブジェクトは、I/O 待機時間中にインターフェイス呼び出しを行えるようにすることで、クライアントに最大応答を提供する MTA をサポートすることを選択できます。 または、ほとんどの場合、ユーザーと対話するオブジェクトは、受信 COM 呼び出しを GUI 操作と同期するために STA をサポートすることを選択します。 COM では同期が提供されるため、STA モデルをサポートする方が簡単です。 オブジェクトは同期を実装する必要があるため、MTA モデルのサポートは難しくなりますが、COM によって提供されるインターフェイス呼び出し全体ではなく、コードの小さなセクションで同期が使用されるため、クライアントへの応答が優れています。

STA モデルは Microsoft Transaction Server (MTS、以前はコード名 "Viper") でも使用されるため、MTS 環境内で実行する予定の DLL ベースのオブジェクトは STA モデルを使用する必要があります。 通常、MTS 環境では、MTA モデル用に実装されたオブジェクトは正常に動作します。 ただし、不要なスレッド同期プリミティブを使用するため、実行効率は低くなります。

In-Proc サーバーのサポートされるスレッドモデルのマーキング

スレッドは、初期化せずに COM を呼び出 CoInitializeEx(NULL, COINIT_MULTITHREADED) すか使用する場合に、MTA モデルを使用します。 スレッドが STA モデルを呼び出す場合、または呼び出す CoInitialize 場合は STA モデルを使用します CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)

CoInitialize API は、クライアント コードとパッケージ化されたオブジェクトのアパートメント制御を提供します。COM ランタイムのスタートアップ コードは必要な方法で COM を初期化できるため、EXE。

ただし、インプロシージャ (DLL ベース) COM サーバーは、DLL サーバーが読み込まれる時点でこれらの API が呼び出されるため、呼び出 CoInitialize/CoInitializeEx しません。 そのため、DLL サーバーはレジストリを使用して、COM がサポートするスレッド モデルを COM に通知して、システムがシステムと互換性のある方法で動作するようにする必要があります。 この目的には、次のように、コンポーネントの CLSID\InprocServer32 キーの ThreadingModel 名前付き値が使用されます。

  • ThreadingModel 値が存在しない: シングル スレッド モデルをサポートします。
  • ThreadingModel=Apartment: STA モデルをサポートします。
  • ThreadingModel=Both: STA および MTA モデルをサポートします。
  • ThreadingModel=Free: MTA のみをサポートします。

注:

ThreadingModel は名前付き値であり、InprocServer32 のサブキーではなく、以前のバージョンの Win32 ドキュメントで誤って文書化されています。

インプロシージャ サーバーのスレッド モデルについては、この記事の後半で説明します。 インプロシージャ サーバーが多数の種類のオブジェクト (それぞれ独自の一意の CLSID を持つ) を提供する場合、各型は異なる ThreadingModel 値を持つことができます。 つまり、スレッド モデルは CLSID ごとであり、コード パッケージ/DLL ごとではありません。 ただし、API エントリ ポイントは、複数のスレッドThreadingModelをサポートするすべてのインプロセス サーバー (つまり、Apartment、Both、DLLCanUnloadNow()または Free) に対してスレッド セーフである必要があります。DLLGetClassObject()

既に説明したように、アウトプロセス サーバーは ThreadingModel 値を使用して自分自身をマークしません。 代わりに、使用 CoInitialize または CoInitializeEx. COM の "サロゲート" 機能 (システム提供のサロゲート DLLHOST.EXEなど) を使用してプロセス外で実行される予定の DLL ベースのサーバーは、DLL ベースのサーバーの規則に従います。その場合、特別な考慮事項はありません。

クライアントとオブジェクトで異なるスレッド モデルを使用する場合

クライアントとプロセス外のオブジェクトの間の相互作用は、クライアントとオブジェクトが異なるプロセスにあり、COM がクライアントからオブジェクトへの呼び出しの受け渡しに関与するため、異なるスレッド モデルが使用されている場合でも、すぐに進みます。 COM はクライアントとサーバーの間に置き換えられるため、スレッド モデルの相互運用のためのコードが提供されます。 たとえば、複数の STA または MTA クライアントによって STA オブジェクトが同時に呼び出される場合、COM は、対応するウィンドウ メッセージをサーバーのメッセージ キューに配置することによって呼び出しを同期します。 オブジェクトの STA は、メッセージを取得してディスパッチするたびに 1 つの呼び出しを受け取ります。 スレッド モデルの相互運用性のすべての組み合わせが許可され、クライアントとプロセス外オブジェクトの間で完全にサポートされます。

クライアントと異なるスレッド モデルを使用するインプロシージャ オブジェクト間の相互作用は、より複雑です。 サーバーはインプロシージャですが、場合によっては、COM はクライアントとオブジェクトの間にそれ自体をインターポーズする必要があります。 たとえば、STA モデルをサポートするように設計されたインプロシージャ オブジェクトは、クライアントの複数のスレッドによって同時に呼び出されることがあります。 COM では、オブジェクトがこのような同時アクセス用に設計されていないため、クライアント スレッドがオブジェクトのインターフェイスに直接アクセスすることはできません。 代わりに、COM は、呼び出しが同期され、オブジェクトを "含む" STA に関連付けられているスレッドによってのみ行われるようにする必要があります。 複雑さが加えられたにもかかわらず、スレッドモデルの相互運用性のすべての組み合わせは、クライアントとインプロシージャ オブジェクトの間で許可されます。

アウトプロセス (EXE ベース) サーバーのスレッド モデル

プロセス外サーバーの 3 つのカテゴリを次に示します。各カテゴリは、そのクライアントが使用するスレッド モデルに関係なく、任意の COM クライアントで使用できます。

  1. STA モデル サーバー:

    サーバーは、1 つ以上の STA で COM を動作させます。 着信呼び出しは COM によって同期され、オブジェクトが作成された STA に関連付けられたスレッドによって配信されます。 クラス ファクトリ メソッド呼び出しは、クラス ファクトリを登録した STA に関連付けられているスレッドによって配信されます。 オブジェクトとクラス ファクトリは、同期を実装する必要はありません。 ただし、実装者は、複数の STA で使用される任意のグローバル変数へのアクセスを同期する必要があります。 サーバーは、インターフェイス ポインターを使用 CoMarshalInterThreadInterfaceInStream して CoGetInterfaceAndReleaseStream マーシャリングする必要があります 。場合によっては他のサーバーから、STA 間でインターフェイス ポインターをマーシャリングする必要があります。 必要に応じて、サーバーは各 STA に実装 IMessageFilter して、COM による通話配信の側面を制御できます。 退化したケースは、1 つの STA で COM が動作するシングル スレッド モデル サーバーです。

  2. MTA モデル サーバー:

    サーバーは 1 つ以上のスレッドで COM を動作させます。そのすべてが MTA に属しています。 呼び出しは COM によって同期されません。 COM はサーバー プロセスにスレッドのプールを作成し、クライアント呼び出しはこれらのスレッドのいずれかによって配信されます。 スレッドは、メッセージを取得してディスパッチする必要はありません。 オブジェクトとクラス ファクトリは、同期を実装する必要があります。 サーバーは、スレッド間でインターフェイス ポインターをマーシャリングする必要はありません。

  3. 混合モデル サーバー:

    詳細については、この記事の「混合スレッド モデル」のセクションを参照してください。

クライアントのスレッド モデル

クライアントには次の 3 つのカテゴリがあります。

  1. STA モデル クライアント:

    クライアントは、1 つ以上の STA に関連付けられたスレッドで COM を動作します。 クライアントは、STA 間でインターフェイス ポインターを使用 CoMarshalInterThreadInterfaceInStream して CoGetInterfaceAndReleaseStream マーシャリングする必要があります。 退化したケースは、1 つの STA で COM を動作させるシングル スレッド モデル クライアントです。 クライアントのスレッドは、送信呼び出しを行うときに、COM によって提供されるメッセージ ループに入ります。 クライアントは、不在時の呼び出しやその他のコンカレンシーの問題を待機している間に、ウィンドウ メッセージのコールバックと処理を管理するために使用 IMessageFilter できます。

  2. MTA モデル クライアント:

    クライアントは 1 つ以上のスレッドで COM を動作し、そのすべてが MTA に属しています。 クライアントは、スレッド間でインターフェイス ポインターをマーシャリングする必要はありません。 クライアントでは使用 IMessageFilterできません。 クライアントのスレッドは、プロセス外のオブジェクトに対して COM 呼び出しを行い、呼び出しが戻ったときに再開するときに中断します。 着信呼び出しは、COM で作成されたマネージド スレッドに到着します。

  3. 混合モデル クライアント:

    詳細については、この記事の「混合スレッド モデル」のセクションを参照してください。

In-proc (DLL ベース) サーバーのスレッド モデル

インプロセス サーバーには 4 つのカテゴリがあり、各サーバーは、そのクライアントで使用されるスレッド モデルに関係なく、任意の COM クライアントで使用できます。 ただし、インプロシージャ サーバーは、スレッド モデルの相互運用性をサポートする場合に実装するカスタム (非システム定義) インターフェイスのマーシャリング コードを提供する必要があります。これは通常、COM がクライアント アパートメント間のインターフェイスをマーシャリングする必要があるためです。 次の 4 つのカテゴリがあります。

  1. 単一のスレッド処理 ("main" STA) をサポートする In-proc Server- 値がありません ThreadingModel

    このサーバーによって提供されるオブジェクトは、作成されたのと同じクライアント STA によってアクセスされる必要があります。 さらに、サーバーは、同じスレッド (メイン STA に関連付けられているもの) によってアクセスされるグローバル データなどDllGetClassObjectDllCanUnloadNow、すべてのエントリ ポイントを想定しています。 COM でマルチスレッドが導入される前に存在していたサーバーは、このカテゴリに含まれています。 これらのサーバーは複数のスレッドからアクセスするようには設計されていないため、COM はプロセスのメイン STA でサーバーによって提供されるすべてのオブジェクトを作成し、オブジェクトへの呼び出しはメイン STA に関連付けられたスレッドによって配信されます。 他のクライアント アパートメントでは、プロキシを介してオブジェクトにアクセスできます。 他のアパートメントからの呼び出しは、プロキシからメイン STA のスタブ (スレッド間マーシャリング) に移動し、次にオブジェクトに対して行われます。 このマーシャリングにより、COM はオブジェクトの呼び出しを同期でき、呼び出しはオブジェクトが作成された STA によって配信されます。 スレッド間マーシャリングは直接呼び出しに比べて低速であるため、複数の STA (カテゴリ 2) をサポートするためにこれらのサーバーを書き換える必要があります。

  2. シングルスレッド アパートメント モデル (複数の STA) をサポートする In-proc Server - 次のマークが付いています ThreadingModel=Apartment

    このサーバーによって提供されるオブジェクトは、作成されたのと同じクライアント STA によってアクセスされる必要があります。 そのため、シングル スレッドインプロシージャ サーバーによって提供されるオブジェクトに似ています。 ただし、このサーバーによって提供されるオブジェクトはプロセスの複数の STA に作成できるため、サーバーはマルチスレッドで使用するためにエントリ ポイント (およびグローバル データなどDllGetClassObjectDllCanUnloadNow) を設計する必要があります。 たとえば、プロセスの 2 つの STA が同時にインプロシージャ オブジェクトの 2 つのインスタンスを作成する場合、 DllGetClassObject 両方の STA によって同時に呼び出されることがあります。 同様に、 DllCanUnloadNow コードがサーバーで実行中の間にサーバーがアンロードされないように、書き込む必要があります。

    サーバーがクラス ファクトリのインスタンスを 1 つだけ提供してすべてのオブジェクトを作成する場合は、クラス ファクトリの実装も、複数のクライアント STA によってアクセスされるため、マルチスレッドで使用するように設計する必要があります。 サーバーが呼び出されるたびにクラス ファクトリ DllGetClassObject の新しいインスタンスを作成する場合、クラス ファクトリはスレッド セーフである必要はありません。 ただし、実装者は、すべてのグローバル変数へのアクセスを同期する必要があります。

    クラス ファクトリによって作成された COM オブジェクトは、スレッド セーフである必要はありません。 ただし、グローバル変数へのアクセスは、実装者によって同期する必要があります。 スレッドによって作成されると、オブジェクトは常にそのスレッドを介してアクセスされ、オブジェクトへのすべての呼び出しは COM によって同期されます。 オブジェクトが作成された STA とは異なるクライアント アパートメントは、プロキシを介してオブジェクトにアクセスする必要があります。 これらのプロキシは、クライアントがアパートメント間のインターフェイスをマーシャリングするときに作成されます。

    クラス ファクトリを使用して STA オブジェクトを作成するすべてのクライアントは、オブジェクトへの直接ポインターを取得します。 これはシングル スレッドインプロセス オブジェクトとは異なり、クライアントのメイン STA のみがオブジェクトへの直接ポインターを取得し、オブジェクトを作成する他のすべての STA がプロキシを介してオブジェクトにアクセスします。 スレッド間マーシャリングは直接呼び出しに比べて低速であるため、シングル スレッドインプロシージャ サーバーを変更して複数の STA をサポートすることで速度を向上させることができます。

  3. MTA のみをサポートする In-proc Server - 次のマークが付いています ThreadingModel=Free

    このサーバーによって提供されるオブジェクトは、MTA でのみ安全です。 独自の同期を実装し、同時に複数のクライアント スレッドによってアクセスされます。 このサーバーには、STA モデルと互換性のない動作が存在する場合があります。 (たとえば、STA のメッセージ ポンプを中断する方法で Windows メッセージ キューを使用した場合など)。また、オブジェクトのスレッド モデルを "Free" としてマークすることで、オブジェクトの実装者は次のように述べています。このオブジェクトは任意のクライアント スレッドから呼び出すことができますが、このオブジェクトは、作成したスレッドに直接 (マーシャリングなしで) インターフェイス ポインターを渡すこともでき、これらのスレッドはこれらのポインターを介して呼び出すことができます。 したがって、クライアントがクライアント実装オブジェクト (シンクなど) にインターフェイス ポインターを渡してこのオブジェクトに渡す場合は、作成した任意のスレッドからこのインターフェイス ポインターを使用してコールバックすることを選択できます。 クライアントが STA の場合、シンク オブジェクトを作成したスレッドとは異なるスレッドからの直接呼び出しがエラーになります (上記の 2 つを参照)。 そのため、COM は常に、STA に関連付けられているスレッド内のクライアントがプロキシを介してのみこの種類のインプロシージャ オブジェクトにアクセスできるようにします。 また、これらのオブジェクトは、STA スレッドで直接実行できるため、フリー スレッド マーシャラーと集計しないでください。

  4. アパートメント モデルとフリー スレッドをサポートする In-proc Server - 次のマークが付いています ThreadingModel=Both

    このサーバーによって提供されるオブジェクトは、独自の同期を実装し、複数のクライアント アパートメントによって同時にアクセスされます。 さらに、このオブジェクトは、プロキシではなく、クライアント プロセスの STA または MTA で直接作成および使用されます。 このオブジェクトは STA で直接使用されるため、サーバーはスレッド間でオブジェクトのインターフェイスをマーシャリングする必要があるため、スレッドに適した方法で任意のオブジェクトへのアクセスが保証されます。 また、オブジェクトのスレッド モデルを "両方" としてマークすることで、オブジェクトの実装者は次のように述べています。このオブジェクトは任意のクライアント スレッドから呼び出すことができますが、このオブジェクトからクライアントへのコールバックは、オブジェクトがコールバック オブジェクトへのインターフェイス ポインターを受け取ったアパートメントでのみ実行されます。 COM では、このようなオブジェクトを STA およびクライアント プロセスの MTA で直接作成できます。

    このようなオブジェクトを作成するアパートメントは常にプロキシ ポインターではなく直接ポインターを取得するため、 ThreadingModel "Both" STA に読み込まれると、オブジェクトのパフォーマンスが向上 ThreadingModel "Free" します。

    ThreadingModel "Both"オブジェクトは MTA アクセス用にも設計されているため (内部的にはスレッド セーフです)CoCreateFreeThreadedMarshaler、. このシステム指定のオブジェクトは、呼び出し元のオブジェクトに集約され、カスタム マーシャリングは、プロセス内のすべてのアパートメントにオブジェクトへのポインターを直接マーシャリングします。 STA または MTA を問わず、任意のアパートメント内のクライアントは、プロキシを介してではなくオブジェクトに直接アクセスできます。 たとえば、STA モデル クライアントは STA1 でインプロシージャ オブジェクトを作成し、そのオブジェクトを STA2 にマーシャリングします。 オブジェクトがフリースレッド マーシャラーと集計されない場合、STA2 はプロキシを介してオブジェクトへのアクセスを取得します。 その場合、フリースレッド マーシャラーは STA2 にオブジェクトへの直接ポインターを提供します。

    注:

    フリースレッド マーシャラーを使用して集計する場合は注意が必要です。 例として、"Apartment" である別のオブジェクトへのインターフェイス ポインターである ThreadingModel "Both" データ メンバーがマークされている (また、フリー スレッド マーシャラーを使用して集計する) オブジェクトがあると ThreadingModel します。 次に、STA が最初のオブジェクトを作成し、作成時に最初のオブジェクトが 2 番目のオブジェクトを作成するとします。 上で説明した規則に従って、最初のオブジェクトは 2 番目のオブジェクトへの直接ポインターを保持しています。 ここで、STA が最初のオブジェクトへのインターフェイス ポインターを別のアパートメントにマーシャリングするとします。 最初のオブジェクトはフリー スレッド マーシャラーで集計されるため、最初のオブジェクトへの直接ポインターが 2 番目のアパートメントに与えられます。 2 番目のアパートメントがこのポインターを介して呼び出され、この呼び出しによって最初のオブジェクトが 2 番目のオブジェクトへのインターフェイス ポインターを介して呼び出される場合、2 番目のオブジェクトは 2 番目のアパートメントから直接呼び出されることを意図していないため、エラーが発生しました。 最初のオブジェクトが直接ポインターではなく、2 番目のオブジェクトへのプロキシへのポインターを保持している場合は、別のエラーが発生します。 システム プロキシは、1 つだけのアパートメントに関連付けられている COM オブジェクトでもあります。 特定の回覧を避けるために、彼らはアパートを追跡します。 そのため、オブジェクトが実行されているスレッドとは別のアパートメントに関連付けられているプロキシで呼び出すオブジェクトは、プロキシから返されるRPC_E_WRONG_THREADを受け取り、呼び出しは失敗します。

クライアントとインプロセス オブジェクト間のスレッド モデルの相互運用性

スレッド モデルの相互運用性のすべての組み合わせは、クライアントとインプロセス オブジェクトの間で許可されます。

COM を使用すると、すべての STA モデル クライアントが、クライアントのメイン STA 内のオブジェクトを作成してアクセスし、呼び出 CoCreateInstance[Ex]したクライアント STA にマーシャリングすることで、単一スレッドのインプロセス オブジェクトと相互運用できます。

クライアント内の MTA が STA モデルインプロセス サーバーを作成する場合、COM はクライアントで "ホスト" STA を起動します。 このホスト STA によってオブジェクトが作成され、インターフェイス ポインターが MTA にマーシャリングされます。 同様に、STA によって MTA インプロシージャ サーバーが作成されると、COM によってホスト MTA が起動され、オブジェクトが作成され、STA にマーシャリングされます。 単一スレッド モデルと MTA モデル間の相互運用性も同様に処理されます。これは、単一スレッド モデルは STA モデルの退縮的なケースに過ぎないためです。

クライアント アパートメント間のインターフェイスをマーシャリングするために COM を必要とする相互運用性をサポートする場合は、インプロシージャ サーバーが実装するすべてのカスタム インターフェイスに対してマーシャリング コードを指定する必要があります。 詳細については、以下の「参照」セクションを参照してください。

スレッド モデルと返されるクラス ファクトリ オブジェクトの間のリレーションシップ

次の 2 つの手順では、アパートメントに "読み込む" インプロセス サーバーの正確な定義について説明します。

  1. in-proc サーバー クラスを含む DLL がオペレーティング システム ローダーによって以前に読み込まれていない (プロセス アドレス空間にマップされている) 場合、その操作が実行され、COM は DLL によってエクスポートされた関数の DLLGetClassObject アドレスを取得します。 DLL が以前に任意のアパートメントに関連付けられているスレッドによって読み込まれている場合、このステージはスキップされます。

  2. COM では、"読み込み" アパートメントに関連付けられているスレッド (または MTA の場合はスレッドの 1 つ) を使用して、DLL によってエクスポートされた関数を呼び出 DllGetClassObject して、必要なクラスの CLSID を要求します。 返されたファクトリ オブジェクトは、クラスのオブジェクトのインスタンスを作成するために使用されます。

    2 番目の DllGetClassObject 手順 (COM による呼び出し) は、同じアパートメント内からでも、クライアントが呼び出す CoGetClassObject/CoCreateIntance[Ex]たびに発生します。 言い換えると、 DllGetClassObject 同じアパートメントに関連付けられているスレッドによって何度も呼び出される場合があります。すべてのクライアントが、そのクラスのクラス ファクトリ オブジェクトにアクセスしようとしているクライアントの数によって異なります。

クラス実装の作成者であり、最終的には、特定のクラス セットの DLL パッケージの作成者は、関数呼び出しに応答して返すファクトリ オブジェクトを完全に自由に DllGetClassObject 決定できます。 DLL パッケージの作成者は、1 つの DLL 全体 DllGetClassObject() のエントリ ポイントの背後にあるコードが、何を行うかを決定する最初かつ潜在的に最終的な権利を持っているため、最終的な言い分を持っています。 次の 3 つの一般的な可能性があります。

  1. DllGetClassObject は、呼び出し元スレッドごとに一意のクラス ファクトリ オブジェクトを返します (つまり、STA あたり 1 つのクラス ファクトリ オブジェクトと、MTA 内の複数のクラス ファクトリを意味します)。

  2. DllGetClassObject 呼び出し元のスレッドの ID や呼び出し元スレッドに関連付けられているアパートメントの種類に関係なく、常に同じクラス ファクトリ オブジェクトを返します。

  3. DllGetClassObject は、呼び出し元のアパートメントごとに一意のクラス ファクトリ オブジェクトを返します (STA と MTA の両方のアパートメントあたり 1 つ)。

呼び出しと返されるクラス ファクトリ オブジェクト (呼び出し DllGetClassObject ごとに新しいクラス ファクトリ オブジェクトなど) の間のリレーションシップには他にも DllGetClassObject可能性がありますが、現在は役に立たないようです。

In-Proc サーバーのクライアント およびオブジェクト スレッド モデルの概要

次の表は、クライアント スレッドが最初にインプロセス サーバーとして実装されているクラスを呼び出 CoGetClassObject したときの、さまざまなスレッド モデル間の相互作用をまとめたものです。

クライアント/スレッドの種類:

  • クライアントは、"main" STA に関連付けられたスレッド (最初に呼び出 CoInitialize すスレッドまたは CoInitializeEx フラグを使用 COINIT_APARTMENTTHREADED して) で実行されています。この STA0 (シングル スレッド モデルとも呼ばれます) を呼び出します。
  • クライアントが他の STA [ASCII 150] で関連付けられているスレッドで実行されている場合は、この STA* を呼び出します。
  • クライアントは、MTA に関連付けられているスレッドで実行されています。

DLL サーバーの種類:

  • サーバーにはキーがありません ThreadingModel 。この "None" を呼び出します。
  • サーバーに "Apartment" というマークが付いています。この "Apt" を呼び出します。
  • サーバーは "Free" とマークされています。
  • サーバーは "両方" とマークされます。

次の表を読む場合は、サーバーをアパートメントに "読み込む" という上記の定義に注意してください。

Client         Server                 Result
STA0           None                   Direct access; server loaded into STA0  
STA*           None                   Proxy access; server loaded into STA0.  
MTA            None                   Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary;  
STA0           Apt                    Direct access; server loaded into STA0  
STA*           Apt                    Direct access; server loaded into STA*  
MTA            Apt                    Proxy access; server loaded into an STA created automatically by COM.
STA0           Free                   Proxy access; server is loaded into MTA MTA created automatically by COM if necessary.
STA*           Free                   Same as STA0->Free
MTA            Free                   Direct access
STA0           Both                   Direct access; server loaded into STA0
STA*           Both                   Direct access; server loaded into STA*
MTA            Both                   Direct access; server loaded into the MTA

関連情報

インターフェイスに関する CoRegisterMessageFilter()IMessageFilter SDK ドキュメント。