インプロセス サーバーのスレッドの問題

インプロセス サーバーは、スレッド モデルをマークするために CoInitializeCoInitializeEx、または OleInitialize を呼び出しません。 スレッド対応の DLL ベースまたはインプロセス オブジェクトの場合は、レジストリでスレッド モデルを設定する必要があります。 スレッド モデルを指定しない場合の既定のモデルは、プロセスごとに単一スレッドです。 モデルを指定するには、ThreadingModel 値をレジストリの InprocServer32 キーに追加します。

クラス オブジェクトのインスタンス化をサポートする DLL は、DllGetClassObject 関数と DllCanUnloadNow 関数を実装してエクスポートする必要があります。 クライアントが DLL でサポートされているクラスのインスタンスを必要とする場合、CoGetClassObject の呼び出し (直接または CoCreateInstance の呼び出しを通じて) は、DllGetClassObject を呼び出して、オブジェクトが DLL に実装されるときに、そのクラス オブジェクトへのポインターを取得します。 したがって、DllGetClassObject は、複数のクラス オブジェクトまたは単一のスレッド セーフ オブジェクトを提供できる必要があります (基本的には、内部参照カウントで InterlockedIncrement/InterlockedDecrement を使用するだけです)。

その名前が示すように、DllCanUnloadNow が呼び出されて、それを実装する DLL が使用中かどうかを判断し、使用されていない場合は呼び出し元が安全にアンロードできるようにします。 任意のスレッドから CoFreeUnusedLibraries への呼び出しは、常に メイン アパートメントのスレッドを経由して DllCanUnloadNow を呼び出します。

他のサーバーと同様に、インプロセス サーバーはシングルスレッド、アパートメント スレッド、またはフリースレッドにすることができます。 これらのサーバーは、クライアントが使用するスレッド モデルに関係なく、どの OLE クライアントでも使用できます。

クライアントとインプロセス オブジェクトの間では、スレッド モデルの相互運用性のすべての組み合わせが許可されます。 異なるスレッド モデルを使用するクライアントとインプロセス オブジェクト間の対話は、クライアントとアウトプロセス サーバー間の対話とまったく同じです。 インプロセス サーバーの場合、クライアントとインプロセス サーバーのスレッド モデルが異なる場合、COM がクライアントとオブジェクトの間に介在する必要があります。

シングルスレッド モデルをサポートするインプロセス オブジェクトがクライアントの複数のスレッドによって同時に呼び出される場合、COM ではクライアント スレッドがオブジェクトのインターフェイスに直接アクセスすることを許可できません。オブジェクトはそのようなアクセス用に設計されていません。 代わりに、COM は呼び出しが同期され、オブジェクトを作成したクライアント スレッドによってのみ行われることを保証する必要があります。 したがって、COM はクライアントのメイン アパートメントにオブジェクトを作成し、他のすべてのクライアント アパートメントがプロキシを使用してオブジェクトにアクセスすることを要求します。

クライアントのフリースレッド アパートメント (マルチスレッド アパートメント モデル) がアパートメント スレッドのインプロセス サーバーを作成すると、COM はクライアントでシングルスレッドのアパートメント モデルの「ホスト」スレッドを起動します。 このホスト スレッドはオブジェクトを作成し、インターフェイス ポインターはクライアントのフリースレッド アパートメントにマーシャリングされて戻されます。 同様に、アパートメント モデル クライアントのシングルスレッド アパートメントがフリースレッド インプロセス サーバーを作成すると、COM はフリースレッド ホスト スレッド (オブジェクトが作成され、その後クライアントのシングルスレッド アパートメントにマーシャル バックされるマルチスレッド アパートメント) をスピンアップします。

Note

一般に、インプロセス サーバー上でカスタム インターフェイスを設計する場合は、COM がクライアント アパートメント間のインターフェイスをマーシャリングできるように、そのマーシャリング コードも提供する必要があります。

 

COM は、オブジェクトが作成されたのと同じクライアント アパートメントからのアクセスを要求することで、シングル スレッド DLL によって提供されるオブジェクトへのアクセスを保護します。 さらに、すべての DLL エントリ ポイント (DllGetClassObjectDllCanUnloadNow など) とグローバル データには、常に同じアパートメントからアクセスする必要があります。 COM は、クライアントのメイン アパートメントにこのようなオブジェクトを作成し、メイン アパートメントにオブジェクトのポインターへの直接アクセスを提供します。 他のアパートメントからの呼び出しは、スレッド間マーシャリングを使用して、プロキシからメイン アパートメントのスタブに到達し、その後オブジェクトに到達します。 これにより、COM はオブジェクトへの呼び出しを同期できます。 スレッド間呼び出しは遅いため、複数のアパートメントをサポートするようにこれらのサーバーを書き直すことをお勧めします。

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

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

クラス ファクトリによって作成されたオブジェクトはスレッド セーフである必要はありません。 スレッドによってオブジェクトが作成されると、オブジェクトは常にそのスレッドを通じてアクセスされ、オブジェクトへのすべての呼び出しは COM によって同期されます。 このオブジェクトを作成するクライアントのアパートメント モデル アパートメントは、オブジェクトへの直接ポインターを取得します。 オブジェクトが作成されたアパートメントとは異なるクライアント アパートメントは、プロキシ経由でオブジェクトにアクセスする必要があります。 これらのプロキシは、クライアントがアパートメント間のインターフェイスをマーシャリングするときに作成されます。

インプロセス DLL ThreadingModel 値が「Both」に設定されている場合、この DLL によって提供されるオブジェクトは、シングル スレッドまたはマルチスレッドのクライアント アパートメントで直接 (プロキシなしで) 作成および使用できます。 ただし、直接使用できるのは、それが作成されたアパート内でのみです。 オブジェクトを他のアパートメントに渡すには、オブジェクトをマーシャリングする必要があります。 DLL オブジェクトは独自の同期を実装する必要があり、複数のクライアント アパートメントから同時にアクセスできます。

プロセス内 DLL オブジェクトへのフリー スレッド アクセスのパフォーマンスを高速化するために、COM は CoCreateFreeThreadedMarshaler 関数を提供します。 この関数は、インプロセス サーバー オブジェクトと集約できるフリースレッド マーシャリング オブジェクトを作成します。 同じプロセス内のクライアント アパートメントが別のアパートメント内のオブジェクトにアクセスする必要がある場合、フリースレッド マーシャラーを集約することで、クライアントがオブジェクトのインターフェイスを別のアパートメントにマーシャルするときに、プロキシではなく、サーバー オブジェクトへの直接のポインターをクライアントに提供します。 クライアントは同期を行う必要はありません。 これは同じプロセス内でのみ機能します。他のプロセスに送られるオブジェクトへの参照には、標準的なマーシャリングが使われます。

フリー スレッドのみをサポートするプロセス内 DLL によって提供されるオブジェクトは、フリースレッド オブジェクトです。 これは独自の同期を実装しており、複数のクライアント スレッドから同時にアクセスできます。 このサーバはスレッド間のインターフェイスをマーシャルしないので、このサーバはクライアントのマルチスレッド アパートメントによってのみ、(プロキシなしで) 直接作成および使用することができます。 作成したシングルスレッド アパートメントは、プロキシを介してアクセスします。

アパートメント間のインターフェイスへのアクセス

スレッド モデルの選択

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

プロセス、スレッド、およびアパートメント

シングル スレッドおよびマルチスレッド コミュニケーション

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