Scrittura di un surrogato personalizzato

Mentre 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 dallo stesso processo del client, il server DLL non funzionerà correttamente se è in esecuzione nel surrogato del sistema. Un surrogato personalizzato può essere personalizzato per 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 gratuito e apartment. Un surrogato personalizzato può essere personalizzato per caricare solo le DLL di appartamento per motivi di efficienza o per accettare un argomento della riga di comando per il tipo di DLL consentito per il caricamento.
  • Un surrogato personalizzato potrebbe accettare parametri della riga di comando aggiuntivi che il surrogato di sistema non è.
  • Il surrogato di sistema chiama CoInitializeSecurity e lo indica di usare le impostazioni di sicurezza esistenti trovate nella chiave AppID nel Registro di sistema. Un surrogato personalizzato potrebbe 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 all'interfaccia di essere remota.

Il thread surrogato principale deve in genere eseguire la procedura di configurazione seguente:

  1. Chiamare CoInitializeEx per inizializzare il thread e impostare il modello di threading.
  2. Se si desidera che i server DLL eseguiti nel server possano usare le impostazioni di sicurezza nella chiave del Registro di sistema AppID , chiamare CoInitializeSecurity con la funzionalità di 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 il thread principale in un ciclo per chiamare periodicamente CoFreeUnusedLibraries .
  6. Quando COM chiama ISurrogate::FreeSurrogate, revocare tutte le factory di classe e uscire.

Un processo surrogato deve implementare l'interfaccia ISurrogate . Questa interfaccia deve essere registrata quando viene avviato un nuovo surrogato e dopo aver chiamato CoInitializeEx. Come indicato nei passaggi precedenti, l'interfaccia ISurrogate ha due metodi che chiama COM: 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 prima creare un oggetto class factory che supporta IUnknown, IClassFactory e IMarshal e quindi chiamare CoRegisterClassObject per registrare l'oggetto come classe factory per il CLSID richiesto.

La class factory registrata dal processo surrogato non è la factory di classi effettiva implementata dal server DLL, ma è una factory di classi generica implementata dal processo surrogato che supporta IClassFactory e IMarshal. Poiché è la classe factory del surrogato, anziché quella del server DLL registrato, la classe factory del surrogato dovrà usare la factory di classi 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;
}
 

La classe factory del surrogato deve anche supportare IMarshal perché una chiamata a CoGetClassObject può richiedere qualsiasi interfaccia dalla factory di classi registrata, non solo IClassFactory. Inoltre, poiché la classe factory generica supporta solo IUnknown e IClassFactory, le richieste per altre interfacce devono essere indirizzate all'oggetto reale. Pertanto, dovrebbe essere presente un metodo MarshallalInterface 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 di 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