Schreiben eines benutzerdefinierten Surrogates

Während das systembezogene Surrogate für die meisten Situationen mehr als ausreichend ist, gibt es einige Fälle, in denen das Schreiben eines benutzerdefinierten Surrogates sinnvoll sein könnte. Nachstehend sind einige Beispiele aufgeführt:

  • Ein benutzerdefiniertes Surrogate könnte einige Optimierungen oder Semantik bereitstellen, die nicht im System surrogate vorhanden sind.
  • Wenn eine IN-Process-DLL Code enthält, der vom gleichen Prozess wie dem Client abhängt, funktioniert der DLL-Server nicht ordnungsgemäß, wenn er im System surrogate ausgeführt wird. Ein benutzerdefiniertes Surrogate könnte auf eine bestimmte DLL zugeschnitten werden, um dies zu behandeln.
  • Das System-Surrogate unterstützt ein Mixed-Threading-Modell, damit es sowohl freie als auch Apartmentmodell-DLLs laden kann. Ein benutzerdefiniertes Surrogate kann angepasst werden, um nur Wohnungs-DLLs aus Gründen der Effizienz zu laden oder ein Befehlszeilenargument für den Typ der DLL zu akzeptieren, die geladen werden darf.
  • Ein benutzerdefiniertes Surrogate könnte zusätzliche Befehlszeilenparameter übernehmen, die das System surrogate nicht ausführt.
  • Das System surrogate ruft CoInitializeSecurity auf und teilt ihm mit, dass vorhandene Sicherheitseinstellungen unter dem AppID-Schlüssel in der Registrierung verwendet werden. Ein benutzerdefiniertes Surrogate könnte einen anderen Sicherheitskontext verwenden.
  • Schnittstellen, die nicht remotable sind (z. B. für aktuelle OCXs), funktionieren nicht mit dem System surrogate. Eine benutzerdefinierte Surrogate könnte die DLL-Schnittstellen mit ihrer eigenen Implementierung umschließen und Proxy-/Stub-DLLs mit einer Remotable-IDL-Definition verwenden, die die Schnittstelle remoted ermöglichen würde.

Der Haupt-Surrogate-Thread sollte in der Regel die folgenden Setupschritte ausführen:

  1. Rufen Sie CoInitializeEx auf, um den Thread zu initialisieren und das Threadingmodell festzulegen.
  2. Wenn Sie möchten, dass die DLL-Server, die auf dem Server ausgeführt werden, die Sicherheitseinstellungen im AppID-Registrierungsschlüssel verwenden können, rufen Sie CoInitializeSecurity mit der EOAC_APPID-Funktion auf. Andernfalls werden ältere Sicherheitseinstellungen verwendet.
  3. Rufen Sie CoRegisterSurrogate auf, um die Surrogate-Schnittstelle für COM zu registrieren.
  4. Rufen Sie ISurrogate::LoadDllServer für die angeforderte CLSID auf.
  5. Setzen Sie Hauptthread in eine Schleife, um CoFreeUnusedLibraries regelmäßig zu aufrufen.
  6. Wenn COM ISurrogate aufruft::FreeSurrogate, widerrufen Sie alle Klassenfabriken und beenden.

Ein Surrogate-Prozess muss die ISurrogate-Schnittstelle implementieren. Diese Schnittstelle sollte registriert werden, wenn ein neues Surrogate gestartet wird und nach dem Aufrufen von CoInitializeEx. Wie in den vorherigen Schritten angegeben, weist die ISurrogate-Schnittstelle zwei Methoden auf, die COM aufruft: LoadDllServer, um neue DLL-Server dynamisch in vorhandene Surrogates zu laden; und FreeSurrogate, um die Surrogate frei zu geben.

Die Implementierung von LoadDllServer, die COM mit einer Ladeanforderung aufruft, muss zuerst ein Klassenfabrikobjekt erstellen, das IUnknown, IClassFactory und IMarshal unterstützt, und dann CoRegisterClassObject aufrufen, um das Objekt als Klassenfabrik für die angeforderte CLSID zu registrieren.

Die vom Surrogate-Prozess registrierte Klassenfabrik ist nicht die tatsächliche Klassenfabrik, die vom DLL-Server implementiert wird, sondern eine generische Klassenfabrik, die vom Surrogate-Prozess implementiert wird, der IClassFactory und IMarshal unterstützt. Da es sich um die Klassenfabrik des Surrogates handelt und nicht um den DLL-Server, der registriert wird, muss die Klassenfabrik des Surrogates die reale Klassenfabrik verwenden, um eine Instanz des Objekts für die registrierte CLSID zu erstellen. Die IClassFactory des Surrogates::CreateInstance sollte wie das folgende Beispiel aussehen:

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;
}
 

Die Klassenfabrik des Surrogates muss auch IMarshal unterstützen, da ein Aufruf von CoGetClassObject eine beliebige Schnittstelle aus der registrierten Klassenfabrik anfordern kann, nicht nur IClassFactory. Da die generische Klassenfabrik nur IUnknown und IClassFactory unterstützt, müssen Anforderungen für andere Schnittstellen an das eigentliche Objekt weitergeleitet werden. Daher sollte es eine MarshalInterface-Methode geben, die ähnlich wie folgt sein sollte:

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;
 

Die Surrogate, die einen DLL-Server enthält, muss das KLASSENobjekt des DLL-Servers mit einem Aufruf von CoRegisterClassObject veröffentlichen. Alle Klassenfabriken für DLL-Surrogate sollten als REGCLS_SURROGATE registriert werden. REGCLS_SINGLUSE und REGCLS_MULTIPLEUSE sollten nicht für DLL-Server verwendet werden, die in Surrogate geladen wurden.

Folgen dieser Richtlinien zum Erstellen eines Surrogateprozesses, wenn dies erforderlich ist, sollte das richtige Verhalten sichergestellt werden.

DLL-Surrogates