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

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

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