EternalWindows
COMアプリケーション / プロキシ/スタブ DLL

アウトプロセスサーバーで作成されたオブジェクトは、 クライアントからすれば別プロセスで作成されたオブジェクトということになります。 このような別プロセスのオブジェクトは、当然ながらクライアントのアドレス空間に存在しないわけですが、 それなのにメソッドの呼び出しが成立するのは何故でしょうか。 実はCLSCTX_LOCAL_SERVERを指定したCoCreateInstanceでは、COMによって作成されたプロキシが返ることになっており、 クライアントはこのプロキシのメソッドを呼び出すことになります。 呼び出しを受けたプロキシは、クライアントから渡された引数を適切にマーシャリングし、 RPCを通じてサーバーのスタブへ送信します。 これを受信したサーバーのスタブはデータをアンマーシャリングし、 サーバーのオブジェクトのメソッドを実際に呼び出します。 そして、処理結果をクライアントのプロキシに返します。

プロキシとスタブは、クライアントが使用するインターフェースと同じメソッドをサポートしていなければならないため、 プロキシとスタブの実装はインターフェースによって異なります。 使用するインターフェースに応じて適切なプロキシとスタブを選択するために、 CoCreateInstanceやCoRegisterClassObjectは次のレジストリキーを参照します。

HKEY_CLASSES_ROOT\Interface\{IID}\ProxyStubClsid32

IIDの部分には、使用するインターフェースのIIDが入ります。 ProxyStubClsid32キーの既定のエントリには、プロキシとスタブを提供するオブジェクトのCLSIDが格納されています。 このオブジェクトはインプロセスサーバーとして実装されており、一般にプロキシ/スタブDLLと呼ばれます。 取得したCLSIDを基にHKEY_CLASSES_ROOT\CLSIDを参照すれば、プロキシ/スタブDLLのパスを取得することができるため、 後はこれをロードすればクライアントの場合はプロキシ、サーバーの場合はスタブを用意できたことになります。 次に、プロキシとスタブの関係を示します。

クライアントとサーバーの両方のアドレス空間に、同じプロキシ/スタブDLLがロードされていると考えてください。 クライアントのDLLはプロキシとして機能することになりますが、サーバーのDLLはスタブとして機能します。

IUnknownやIStreamのような既存のインターフェースには、関連するプロキシ/スタブDLLが最初からシステムに存在しています。 よって、既存のインターフェースを使用する場合は、プロキシやスタブについて特に意識する必要はありません。 しかし、開発者が独自に用意したカスタムインターフェースの場合は、プロキシ/スタブDLLが予め用意されていることはありませんから、 開発者がこれを用意することになります。 カスタムインターフェースを使用する場合は、IDLファイルにカスタムインターフェースの定義を記述することになりますが、 これをコンパイルした際にはプロキシ/スタブ用のファイルが作成されることになっています。 よって、これをDLLのプロジェクトに追加してコンパイルすれば、プロキシ/スタブDLLを作成することができます。 次に、プロキシ/スタブDLLのファイル構成を示します。

IDLファイルをプロジェクトに追加してコンパイルすれば、 sample_h.h、sample_p.c、sample_i.c、dlldata.cが作成されることになります。 sample_p.cは、プロキシ/スタブのコードが含まれているため当然必要ですし、 sample_i.cはIIDの定義が含まれているため必要です。 また、dlldata.cにはDLLのエクスポート関数であるDllGetClassObjectなどの実装が含まれているため、これも必要です。 アプリケーションが独自にソースファイルを記述する必要はないのかというと、必ずしもそうとは限りません。 既定では、アプリケーションはDllRegisterServerとDllUnregisterServerを実装し、DLLの登録と解除を行わなければならないようになっています。 しかし、開発環境でREGISTER_PROXY_DLLを定義すると、これらの関数の実装も省略することができるため、 結果として独自のソースファイルは不要になります。

「プリプロセッサの定義」という項目にREGISTER_PROXY_DLLを追加します。 これでコンパイルすると、DllRegisterServerとDllUnregisterServerの実装はDLLに任せることができます。 ちなみに、プロキシ/スタブはrpcrt4.libへのリンクを必要としているため、これも開発環境で設定しておきましょう。

「追加の依存ファイル」にrpcrt4.libを追加するようにします。 これにより、DLLのコンパイルが成功することになります。

作成したプロキシ/スタブDLLは、通常のインプロセスサーバーと同様にregsvr32で登録することができます。

regsvr32 C:\sample.dll (登録の解除時は regsvr32 /u C:\sample.dll のようにする)

これにより、HKEY_CLASSES_ROOT\Interface以下にカスタムインターフェースのIIDのキーが作成され、 HKEY_CLASSES_ROOT\CLSID以下にオブジェクトのCLSID(値はIIDと同一)のキーが作成されます。 このキーの既定のエントリはPSFactoryBufferになっているはずですが、 これはそのサーバーがプロキシ/スタブDLLであることを示す指標です。 PSFactoryBufferという言葉は、プロキシ/スタブDLLのクラスオブジェクトがIPSFactoryBufferを実装していることに由来していると思われます。


戻る