Writing a Custom Surrogate

While the system-supplied surrogate will be more than adequate for most situations, there are some cases where writing a custom surrogate could be worthwhile. Following are some examples.

The main surrogate thread should typically perform the following setup steps:

  1. Call CoInitializeEx to initialize the thread and set the threading model.
  2. If you want the DLL servers that are to run in the server to be able to use the security settings in the AppID registry key, call CoInitializeSecurity with the EOAC_APPID capability. Otherwise, legacy security settings will be used.
  3. Call CoRegisterSurrogate to register the surrogate interface to COM.
  4. Call ISurrogate::LoadDllServer for the requested CLSID.
  5. Put main thread in a loop to call CoFreeUnusedLibraries periodically.
  6. When COM calls ISurrogate::FreeSurrogate , revoke all class factories and exit.

A surrogate process must implement the ISurrogate interface. This interface should be registered when a new surrogate is started and after calling CoInitializeEx. The ISurrogate interface has two methods that COM calls: LoadDllServer, to dynamically load new DLL servers into existing surrogates, and FreeSurrogate to free the surrogate.

The implementation of ISurrogate::LoadDllServer, which COM calls with a load request, must first create a class factory object that supports IUnknown, IClassFactory, and IMarshal, and then call CoRegisterClassObject to register the object as the class factory for the requested CLSID.

The class factory registered by the surrogate process is not the actual class factory implemented by the DLL server, but it is a generic class factory implemented by the surrogate process that supports IClassFactory and IMarshal. Since it is the surrogate's class factory, rather than that of the DLL server that is being registered, the surrogate's class factory will need to use the real class factory to create an instance of the object for the registered CLSID. The surrogate's IClassFactory::CreateInstance should look something like this:

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

The surrogate's class factory must also support IMarshal because a call to CoGetClassObject can request any interface from the registered class factory, not just IClassFactory. Further, since the generic class factory only supports IUnknown and IClassFactory, requests for other interfaces must be directed to the real object. Thus, there should be a MarshalInterface method which should be similar to the following:

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;
 

The surrogate that houses a DLL server must publish the DLL server's class object(s) with a call to CoRegisterClassObject. All class factories for DLL surrogates should be registered as REGCLS_SURROGATE. REGCLS_SINGLUSE and REGCLS_MULTIPLEUSE should not be used for DLL servers loaded into surrogates.

Following these guidelines for creating a surrogate process when it is necessary to do so should ensure proper function.