编写自定义代理项
虽然系统提供的代理项将足以满足大多数情况,但在某些情况下,编写自定义代理项可能值得。 下面是一些示例:
- 自定义代理项可以提供系统代理项中不存在的某些优化或语义。
- 如果进程内 DLL 包含的代码依赖于客户端位于同一进程中,则 DLL 服务器在系统代理项中运行时将无法正常运行。 自定义代理项可以定制为特定 DLL 来处理此问题。
- 系统代理项支持混合线程模型,以便它可以加载免费模型和单元模型 DLL。 出于效率的原因,可以定制自定义代理项以仅加载单元 DLL,或者接受允许加载的 DLL 类型的命令行参数。
- 自定义代理项可以采用系统代理项不具有的额外命令行参数。
- 系统代理项调用 CoInitializeSecurity,并告知其使用注册表中 AppID 键下找到的任何现有安全设置。 自定义代理项可以使用另一个安全上下文。
- 不可远程的接口(如最近 OCX 的接口)不适用于系统代理项。 自定义代理项可以使用自己的实现包装 DLL 的接口,并使用具有可远程 IDL 定义的代理/存根 DLL,从而允许该接口进行远程处理。
主代理项线程通常应执行以下设置步骤:
- 调用 CoInitializeEx 初始化线程并设置线程模型。
- 如果希望服务器中运行的 DLL 服务器能够使用 AppID 注册表项中的安全设置,请使用 EOAC_APPID 功能调用 CoInitializeSecurity。 否则,将使用旧的安全设置。
- 调用 CoRegisterSurrogate 将代理项接口注册到 COM。
- 为请求的 CLSID 调用 ISurrogate::LoadDllServer。
- 将主线程置于循环中,定期调用 CoFreeUnusedLibraries。
- 当 COM 调用 ISurrogate::FreeSurrogate 时,撤销所有类工厂并退出。
代理项进程必须实现 ISurrogate 接口。 应在启动新代理项时以及调用 CoInitializeEx 之后注册此接口。 如前面的步骤所示,ISurrogate 接口有两个 COM 调用的方法:LoadDllServer,用于将新 DLL 服务器动态加载到现有代理项;FreeSurrogate 用于释放代理项。
COM 通过加载请求调用的 LoadDllServer 的实现必须首先创建一个类工厂对象,该对象支持 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 服务器的代理必须通过调用 CoRegisterClassObject 来发布 DLL 服务器的类对象。 DLL 代理项的所有类工厂都应注册为 REGCLS_SURROGATE。 REGCLS_SINGLUSE 和 REGCLS_MULTIPLEUSE 不应该用于加载到代理中的 DLL 服务器。
当需要创建代理项进程时,遵循这些指导方针可以确保正确的行为。
相关主题