カスタム サロゲートの作成

システム提供のサロゲートはほとんどの状況で十分ですが、カスタム サロゲートを記述する価値がある場合もあります。 その例を以下に示します。

  • カスタム サロゲートは、システム サロゲートに存在しないいくつかの最適化またはセマンティクスを提供できます。
  • インプロセス DLL に、クライアントと同じプロセスに依存するコードが含まれている場合、システム サロゲートで実行されている場合、DLL サーバーは正しく機能しません。 カスタム サロゲートは、これに対処するために特定の DLL に合わせて調整できます。
  • システム サロゲートは、フリー モデル DLL とアパートメント モデル DLL の両方を読み込むことができるように、混合スレッド モデルをサポートしています。 効率上の理由からアパートメント DLL のみを読み込むか、読み込みが許可されている DLL の種類に対してコマンド ライン引数を受け入れるように、カスタム サロゲートを調整できます。
  • カスタム サロゲートは、システム サロゲートにはない追加のコマンド ライン パラメーターを受け取る可能性があります。
  • システム サロゲートは CoInitializeSecurity を呼び出し、レジストリの AppID キーの下にある既存のセキュリティ設定を使用するように指示します。 カスタム サロゲートでは、別のセキュリティ コンテキストを使用できます。
  • リモート処理できないインターフェイス (最近の OCX のインターフェイスなど) は、システム サロゲートでは機能しません。 カスタム サロゲートは、DLL のインターフェイスを独自の実装でラップし、リモート処理可能な IDL 定義を持つプロキシ/スタブ DLL を使用して、インターフェイスをリモート処理できます。

メインサロゲート スレッドは、通常、次のセットアップ手順を実行する必要があります。

  1. CoInitializeEx を呼び出してスレッドを初期化し、スレッド モデルを設定します。
  2. サーバーで実行する DLL サーバーで AppID レジストリ キーのセキュリティ設定を使用できるようにする場合は、EOAC_APPID機能を使用して CoInitializeSecurity を呼び出します。 それ以外の場合は、従来のセキュリティ設定が使用されます。
  3. CoRegisterSurrogate を呼び出して、サロゲート インターフェイスを COM に登録します。
  4. 要求された CLSID に対して ISurrogate::LoadDllServer を呼び出します。
  5. メインスレッドをループに入れて、 CoFreeUnusedLibraries を定期的に呼び出します。
  6. COM が ISurrogate::FreeSurrogate を呼び出す場合は、すべてのクラス ファクトリを取り消して終了します。

サロゲート プロセスでは、 ISurrogate インターフェイスを実装する必要があります。 このインターフェイスは、新しいサロゲートが開始されたときに、および CoInitializeEx を呼び出した後に登録する必要があります。 前のステップで示したように、 ISurrogate インターフェースには 次のような COM が呼び出す2つのメソッドがあります。 LoadDllServer は新しい DLL サーバを既存のサロゲートに動的にロードし、 FreeSurrogate はサロゲートを解放します。

読み込み要求で COM が呼び出す LoadDllServer の実装では、まず IUnknownIClassFactory、および IMarshal をサポートするクラス ファクトリ オブジェクトを作成してから、 CoRegisterClassObject を呼び出して、要求された CLSID のクラス ファクトリとしてオブジェクトを登録する必要があります。

サロゲート プロセスによって登録されたクラス ファクトリは、DLL サーバーによって実装される実際のクラス ファクトリではありませんが、 IClassFactoryIMarshal をサポートするサロゲート プロセスによって実装されるジェネリック クラス ファクトリです。 これは、登録されている DLL サーバーのクラス ファクトリではなく、サロゲートのクラス ファクトリであるため、サロゲートのクラス ファクトリは、実際のクラス ファクトリを使用して、登録済みの CLSID のオブジェクトのインスタンスを作成する必要があります。 サロゲートの IClassFactory::CreateInstance は次の例のようになります。

STDMETHODIMP CSurrogateFactory::CreateInstance(
  IUnknown* pUnkOuter, 
  REFIID iid, 
  void** ppv)
{
    void* pcf;
    HRESULT hr;
 
    hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, &pcf);
    if ( FAILED(hr) )
        return hr;
    hr = ((IClassFactory*)pcf)->CreateInstance(pUnkOuter, iid, ppv);
    ((IClassFactory*)pcf)->Release();
    return hr;
}
 

CoGetClassObject を呼び出すと、 IClassFactory だけでなく、登録済みのクラス ファクトリから任意のインターフェイスを要求できるため、サロゲートのクラス ファクトリも IMarshal をサポートする必要があります。 さらに、ジェネリック クラス ファクトリでは IUnknownIClassFactory のみがサポートされるため、他のインターフェイスに対する要求は実際のオブジェクトに送信する必要があります。 したがって、 次のような MarshalInterface メソッドが必要です。

STDMETHODIMP CSurrogateFactory::MarshalInterface(
  IStream *pStm,  
  REFIID riid, void *pv, 
  WORD dwDestContext, 
  void *pvDestContext, 
  DWORD mshlflags )
{   
    void * pCF = NULL;
    HRESULT hr;
 
    hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, riid, &pCF);
    if ( FAILED(hr) )
        return hr;   
    hr = CoMarshalInterface(pStm, riid, (IUnknown*)pCF, dwDestContext, pvDestContext,  mshlflags);
    ((IUnknown*)pCF)->Release();
    return S_OK;
 

DLL サーバーを格納するサロゲートは、 CoRegisterClassObject を呼び出して DLL サーバーのクラス オブジェクトを発行する必要があります。 DLL サロゲートのすべてのクラス ファクトリは、REGCLS_SURROGATEとして登録する必要があります。 REGCLS_SINGLUStandard EditionとREGCLS_MULTIPLEUStandard Editionは、サロゲートに読み込まれた DLL サーバーには使用しないでください。

サロゲート プロセスを作成する必要がある場合は、次のガイドラインに従って、適切な動作を確認する必要があります。

DLL 代理