Scrittura di un surrogato personalizzato

Anche se il surrogato fornito dal sistema sarà più che adeguato per la maggior parte delle situazioni, ci sono alcuni casi in cui la scrittura di un surrogato personalizzato potrebbe essere utile. Ecco alcuni esempi:

  • Un surrogato personalizzato potrebbe fornire alcune ottimizzazioni o semantiche non presenti nel surrogato di sistema.
  • Se una DLL in-process contiene codice che dipende dall'essere nello stesso processo del client, il server DLL non funzionerà correttamente se è in esecuzione nel surrogato di sistema. Un surrogato personalizzato può essere personalizzato in base a una DLL specifica per gestire questo problema.
  • Il surrogato di sistema supporta un modello di threading misto in modo che possa caricare DLL del modello sia libero che apartment. Un surrogato personalizzato può essere personalizzato per caricare solo DLL apartment per motivi di efficienza o per accettare un argomento della riga di comando per il tipo di DLL che è autorizzato a caricare.
  • Un surrogato personalizzato potrebbe accettare parametri aggiuntivi della riga di comando che il surrogato di sistema non esegue.
  • Il surrogato di sistema chiama CoInitializeSecurity e indica all'utente di usare le impostazioni di sicurezza esistenti presenti nella chiave AppID nel Registro di sistema. Un surrogato personalizzato può usare un altro contesto di sicurezza.
  • Le interfacce che non sono remotabili (ad esempio quelle per ocx recenti) non funzioneranno con il surrogato di sistema. Un surrogato personalizzato potrebbe eseguire il wrapping delle interfacce della DLL con la propria implementazione e usare DLL proxy/stub con una definizione IDL remota che consente l'accesso remoto all'interfaccia.

Il thread surrogato principale deve in genere eseguire i passaggi di installazione seguenti:

  1. Chiamare CoInitializeEx per inizializzare il thread e impostare il modello di threading.
  2. Se si desidera che i server DLL da eseguire nel server possano usare le impostazioni di sicurezza nella chiave del Registro di sistema AppID , chiamare CoInitializeSecurity con la funzionalità EOAC_APPID. In caso contrario, verranno usate le impostazioni di sicurezza legacy.
  3. Chiamare CoRegisterSurrogate per registrare l'interfaccia surrogata in COM.
  4. Chiamare ISurrogate::LoadDllServer per il CLSID richiesto.
  5. Inserire periodicamente il thread principale in un ciclo per chiamare CoFreeUnusedLibraries .
  6. Quando COM chiama ISurrogate::FreeSurrogate, revocare tutte le class factory e uscire.

Un processo surrogato deve implementare l'interfaccia ISurrogate . Questa interfaccia deve essere registrata all'avvio di un nuovo surrogato e dopo aver chiamato CoInitializeEx. Come indicato nei passaggi precedenti, l'interfaccia ISurrogate ha due metodi che COM chiama: LoadDllServer, per caricare dinamicamente nuovi server DLL in surrogati esistenti; e FreeSurrogate, per liberare il surrogato.

L'implementazione di LoadDllServer, che chiama COM con una richiesta di caricamento, deve innanzitutto creare un oggetto factory di classe che supporti IUnknown, IClassFactory e IMarshal e quindi chiamare CoRegisterClassObject per registrare l'oggetto come class factory per il CLSID richiesto.

La class factory registrata dal processo surrogato non è la class factory effettiva implementata dal server DLL, ma è una class factory generica implementata dal processo surrogato che supporta IClassFactory e IMarshal. Poiché si tratta della class factory del surrogato, anziché di quella del server DLL in fase di registrazione, la class factory del surrogato dovrà usare la class factory reale per creare un'istanza dell'oggetto per il CLSID registrato. L'IClassFactory::CreateInstance del surrogato dovrebbe essere simile all'esempio seguente:

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

Anche la class factory del surrogato deve supportare IMarshal perché una chiamata a CoGetClassObject può richiedere qualsiasi interfaccia dalla class factory registrata, non solo IClassFactory. Inoltre, poiché la class factory generica supporta solo IUnknown e IClassFactory, le richieste per altre interfacce devono essere indirizzate all'oggetto reale. Pertanto, dovrebbe essere presente un metodo MarshalInterface simile al seguente:

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;
 

Il surrogato che ospita un server DLL deve pubblicare gli oggetti classe del server DLL con una chiamata a CoRegisterClassObject. Tutte le class factory per i surrogati DLL devono essere registrate come REGCLS_SURROGATE. REGCLS_SINGLUSE e REGCLS_MULTIPLEUSE non devono essere usati per i server DLL caricati in surrogati.

Seguendo queste linee guida per la creazione di un processo surrogato quando è necessario farlo, deve garantire un comportamento appropriato.

Surrogati DLL