Написание пользовательского суррогата

Хотя суррогат, предоставляемый системой, будет более чем достаточно для большинства ситуаций, есть некоторые случаи, когда написание пользовательского суррогата может быть стоит. Ниже приводятся некоторые примеры.

  • Пользовательский суррогат может обеспечить некоторые оптимизации или семантику, не присутствующих в суррогатной системе.
  • Если библиотека DLL в процессе содержит код, который зависит от того, что находится в том же процессе, что и клиент, сервер DLL не будет работать правильно, если он работает в системе суррогат. Пользовательский суррогат может быть адаптирован к определенной библиотеке DLL для решения этой проблемы.
  • Суррогат системы поддерживает модель смешанного потока, чтобы она могли загружать как бесплатные, так и библиотеки DLL моделей квартир. Настраиваемый суррогат может быть адаптирован для загрузки только библиотек DLL квартиры по соображениям эффективности или принять аргумент командной строки для типа БИБЛИОТЕК DLL, который разрешено загрузить.
  • Пользовательский суррогат может принимать дополнительные параметры командной строки, которые система суррогат не выполняет.
  • Системный суррогат вызывает CoInitializeSecurity и сообщает ему использовать все существующие параметры безопасности, найденные в разделе AppID в реестре. Пользовательский суррогат может использовать другой контекст безопасности.
  • Интерфейсы, которые не являются ремотируемыми (например, для последних OCX) не будут работать с суррогатной системой. Пользовательский суррогат может упаковать интерфейсы БИБЛИОТЕКи DLL с собственной реализацией и использовать библиотеки DLL прокси-сервера или заглушки с определением remotable IDL, которое позволит удаленному интерфейсу.

Основной суррогатный поток обычно должен выполнять следующие действия по настройке:

  1. Вызовите CoInitializeEx , чтобы инициализировать поток и задать модель потоков.
  2. Если вы хотите, чтобы серверы DLL, выполняемые на сервере, могли использовать параметры безопасности в разделе реестра AppID , вызовите CoInitializeSecurity с возможностью EOAC_APPID. В противном случае будут использоваться устаревшие параметры безопасности.
  3. Вызовите CoRegisterSurrogate , чтобы зарегистрировать суррогатный интерфейс в COM.
  4. Вызовите ISurrogate::LoadDllServer для запрошенного CLSID.
  5. Поместите основной поток в цикл, чтобы периодически вызывать CoFreeUnusedLibraries.
  6. Когда COM вызывает ISurrogate::FreeSurrogate, отмените все фабрики классов и выход.

Суррогатный процесс должен реализовать интерфейс ISurrogate. Этот интерфейс следует зарегистрировать при запуске нового суррогата и после вызова CoInitializeEx. Как указано на предыдущих шагах, интерфейс ISurrogate имеет два метода, вызывающие COM: LoadDllServer, для динамической загрузки новых dll-серверов в существующие суррогаты; и FreeSurrogate для освобождения суррогата.

Реализация LoadDllServer, которая вызывает COM с запросом нагрузки, должна сначала создать объект фабрики классов, поддерживающий IUnknown, IClassFactory и IMarshal, а затем вызвать CoRegisterClassObject, чтобы зарегистрировать объект в качестве фабрики классов для запрошенного CLSID.

Фабрика классов, зарегистрированная суррогатным процессом, не является фактической фабрикой классов, реализованной сервером DLL, но является фабрикой универсальных классов, реализованной суррогатным процессом, поддерживающим IClassFactory и IMarshal. Так как это фабрика классов суррогата, а не сервер 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;
}
 

Фабрика классов суррогата также должна поддерживать IMarshal, так как вызов CoGetClassObject может запрашивать любой интерфейс из зарегистрированной фабрики классов, а не только IClassFactory. Кроме того, так как фабрика универсальных классов поддерживает только IUnknown и IClassFactory, запросы для других интерфейсов должны направляться в реальный объект. Таким образом, должен быть метод 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, должен публиковать объекты класса DLL-сервера с вызовом CoRegisterClassObject. Все фабрики классов для суррогатов DLL должны быть зарегистрированы как REGCLS_SURROGATE. REGCLS_SINGLUSE и REGCLS_MULTIPLEUSE не следует использовать для серверов DLL, загруженных в суррогаты.

Следуя этим рекомендациям по созданию суррогатного процесса, если это необходимо сделать, должно обеспечить надлежащее поведение.

Суррогаты DLL