次の方法で共有


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、および NT 4.0 および Windows 95 と DCOM95 に存在します。

概要

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

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

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

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

詳細情報

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

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

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

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

プロセスには任意の数の 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 のスレッドは または CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) を呼び出CoInitializeす必要があります。また、ウィンドウ メッセージを取得してディスパッチする必要があります。 詳細については、以下の「REFERENCES」セクションを参照してください。

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

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

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

注:

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

STA モデルでのクライアントの責任:

STA モデルを使用するプロセスまたはスレッドで実行されているクライアント コードは、 と CoGetInterfaceAndReleaseStreamを使用CoMarshalInterThreadInterfaceInStreamして、アパートメント間のオブジェクトのインターフェイスをマーシャリングする必要があります。 たとえば、クライアントの Apartment 1 にインターフェイス ポインターがあり、アパートメント 2 で使用する必要がある場合、アパートメント 1 は を使用して CoMarshalInterThreadInterfaceInStreamインターフェイスをマーシャリングする必要があります。 この関数によって返されるストリーム オブジェクトはスレッド セーフであり、そのインターフェイス ポインターは、アパートメント 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 作業を行う必要がある各スレッドが を呼び出 CoInitializeEx して COM を初期化し、COM 作業が完了したら を呼び出 CoUninitializeすことをお勧めします。 MTA を "不必要に" 初期化するコストは最小限です。

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

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

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

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

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

混合スレッド モデル

混合スレッド モデルをサポートするプロセスでは、1 つの MTA と 1 つ以上の STA が使用されます。 インターフェイス ポインターは、すべてのアパートメント間でマーシャリングする必要がありますが、MTA 内でマーシャリングせずに使用できます。 STA 内のオブジェクトへの呼び出しは COM によって同期され、MTA 内のオブジェクトへの呼び出しは 1 つのスレッドでのみ実行されます。 ただし、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 モデルを使用する必要があります。 MTA モデルに実装されたオブジェクトは、通常、MTS 環境で正常に動作します。 ただし、不要なスレッド同期プリミティブを使用するため、実行効率は低下します。

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

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

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

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

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

注:

ThreadingModel は名前付き値であり、以前のバージョンの Win32 ドキュメントに誤って記載されている InprocServer32 のサブキーではありません。

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

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

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

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

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

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

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

  1. STA モデル サーバー:

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

  2. MTA モデル サーバー:

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

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

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

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

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

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

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

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

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

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

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

インプロセス (DLL ベース) サーバーでのスレッド モデル

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

  1. シングル スレッド ("メイン" STA) をサポートするインプロセス サーバー - 値なしThreadingModel:

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

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

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

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

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

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

  3. MTA のみをサポートするインプロセス サーバー - で ThreadingModel=Freeマークされています。

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

  4. アパートメント モデルとフリー スレッドをサポートするインプロセス サーバー - で ThreadingModel=Bothマークされています。

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

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

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

    注:

    フリー スレッド マーシャラーを使用して集計する場合は、注意が必要です。 たとえば、として ThreadingModel "Both" マークされている (フリー スレッド マーシャラーを使用して集計する) オブジェクトには、"Apartment" を持つ別のオブジェクト ThreadingModel へのインターフェイス ポインターであるデータ メンバーがあるとします。 次に、STA が最初のオブジェクトを作成し、作成時に最初のオブジェクトが 2 番目のオブジェクトを作成するとします。 上で説明した規則に従って、最初のオブジェクトは 2 番目のオブジェクトへの直接ポインターを保持するようになりました。 ここで、STA が最初のオブジェクトへのインターフェイス ポインターを別のアパートメントにマーシャリングすると仮定します。 最初のオブジェクトはフリー スレッド マーシャラーで集計されるため、最初のオブジェクトへの直接ポインターが 2 番目のアパートメントに与えられます。 2 番目のアパートメントがこのポインターを呼び出し、この呼び出しによって 2 番目のオブジェクトへのインターフェイス ポインターを介して呼び出される場合、2 番目のオブジェクトが 2 番目のアパートメントから直接呼び出されるわけではないため、エラーが発生します。 最初のオブジェクトが直接ポインターではなく、2 番目のオブジェクトへのプロキシへのポインターを保持している場合は、別のエラーが発生します。 システム プロキシは、1 つのアパートメントと 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 を必要とする相互運用性をサポートする場合は、インプロセス サーバーが実装するカスタム インターフェイスにマーシャリング コードを提供する必要があります。 詳細については、以下の「REFERENCES」セクションを参照してください。

スレッド モデルとクラス ファクトリ オブジェクトの関係が返されます

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

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

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

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

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

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

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

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

への呼び出しと、返されるクラス ファクトリ オブジェクト (呼び出し DllGetClassObject ごとに新しいクラス ファクトリ オブジェクトなど) の間の関係に関する DllGetClassObject他の可能性がありますが、現時点では役に立たないようです。

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

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

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

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

DLL サーバーの種類:

  • サーバーにはキーがありません ThreadingModel 。この "None" を呼び出します。
  • サーバーは "アパートメント" とマークされています。この "Apt" を呼び出します。
  • サーバーが "無料" とマークされています。
  • サーバーが "両方" とマークされています。

下の表を読むときは、上で行われた定義がアパートにサーバーを "読み込む" という点に注意してください。

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

関連情報

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