编写自定义代理项

虽然系统提供的代理项将足以满足大多数情况,但在某些情况下,编写自定义代理项可能值得。 下面是一些示例:

  • 自定义代理项可以提供系统代理项中不存在的某些优化或语义。
  • 如果进程内 DLL 包含的代码依赖于客户端位于同一进程中,则 DLL 服务器在系统代理项中运行时将无法正常运行。 自定义代理项可以定制为特定 DLL 来处理此问题。
  • 系统代理项支持混合线程模型,以便它可以加载免费模型和单元模型 DLL。 出于效率的原因,可以定制自定义代理项以仅加载单元 DLL,或者接受允许加载的 DLL 类型的命令行参数。
  • 自定义代理项可以采用系统代理项不具有的额外命令行参数。
  • 系统代理项调用 CoInitializeSecurity,并告知其使用注册表中 AppID 键下找到的任何现有安全设置。 自定义代理项可以使用另一个安全上下文。
  • 不可远程的接口(如最近 OCX 的接口)不适用于系统代理项。 自定义代理项可以使用自己的实现包装 DLL 的接口,并使用具有可远程 IDL 定义的代理/存根 DLL,从而允许该接口进行远程处理。

主代理项线程通常应执行以下设置步骤:

  1. 调用 CoInitializeEx 初始化线程并设置线程模型。
  2. 如果希望服务器中运行的 DLL 服务器能够使用 AppID 注册表项中的安全设置,请使用 EOAC_APPID 功能调用 CoInitializeSecurity。 否则,将使用旧的安全设置。
  3. 调用 CoRegisterSurrogate 将代理项接口注册到 COM。
  4. 为请求的 CLSID 调用 ISurrogate::LoadDllServer
  5. 将主线程置于循环中,定期调用 CoFreeUnusedLibraries
  6. 当 COM 调用 ISurrogate::FreeSurrogate 时,撤销所有类工厂并退出。

代理项进程必须实现 ISurrogate 接口。 应在启动新代理项时以及调用 CoInitializeEx 之后注册此接口。 如前面的步骤所示,ISurrogate 接口有两个 COM 调用的方法:LoadDllServer,用于将新 DLL 服务器动态加载到现有代理项;FreeSurrogate 用于释放代理项。

COM 通过加载请求调用的 LoadDllServer 的实现必须首先创建一个类工厂对象,该对象支持 IUnknownIClassFactoryIMarshal,然后调用 CoRegisterClassObject 将该对象注册为所请求 CLSID 的类工厂。

代理项进程注册的类工厂不是 DLL 服务器实现的实际类工厂,而是由支持 IClassFactoryIMarshal 的代理项进程实现的通用类工厂。 因为它是代理项的类工厂,而不是要注册的 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。 此外,由于通用类工厂仅支持 IUnknownIClassFactory,因此必须将其他接口的请求定向到实际对象。 因此,应该有一个 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 服务器的代理必须通过调用 CoRegisterClassObject 来发布 DLL 服务器的类对象。 DLL 代理项的所有类工厂都应注册为 REGCLS_SURROGATE。 REGCLS_SINGLUSE 和 REGCLS_MULTIPLEUSE 不应该用于加载到代理中的 DLL 服务器。

当需要创建代理项进程时,遵循这些指导方针可以确保正确的行为。

DLL 代理项