Winapi 防止进程外服务器在加载进程内服务器对象实例时关闭

Winapi 防止进程外服务器在加载进程内服务器对象实例时关闭,winapi,com,Winapi,Com,我有一个应用程序,它使用进程外COM服务器访问由进程内COM服务器创建的COM对象。这意味着进程外COM服务器必须加载进程内COM DLL以创建最终对象,然后返回该对象 例如: // Create an object which resides in the out of process COM server container.CoCreateInstance("HelperServerProcess"); // Grab a reference to an object which resi

我有一个应用程序,它使用进程外COM服务器访问由进程内COM服务器创建的COM对象。这意味着进程外COM服务器必须加载进程内COM DLL以创建最终对象,然后返回该对象

例如:

// Create an object which resides in the out of process COM server
container.CoCreateInstance("HelperServerProcess");
// Grab a reference to an object which resides in an in process COM server DLL, 
// hosted by the out of process COM server
object = container.GenerateResults();
// Release the object instantiated by the out of process server
container = NULL; // or return, or go out of scope, etc
// This call will fail, because the out of process server has shutdown unloading
// the inproc DLL hosting <object>
object.DoStuff();
//创建驻留在进程外COM服务器中的对象
CoCreateInstance(“HelperServerProcess”);
//获取对驻留在进程内COM服务器DLL中的对象的引用,
//由进程外COM服务器托管
object=container.GenerateResults();
//释放进程外服务器实例化的对象
容器=NULL;//或返回,或超出范围等
//此调用将失败,因为进程外服务器已关闭
//inproc DLL宿主
object.DoStuff();
但是,一旦容器对象被释放,最终的服务器进程引用(在CoReleaseServerProcess中)就会被释放,服务器就会关闭。这将导致在尝试使用结果对象时出现E_RPC_SERVER_UNAVAILABLE错误。同时,此EXE服务器中托管的进程内DLL仍有未完成的对象,因此从CanUnloadNow返回S_FALSE

我认为将IExternalConnection添加到EXE服务器的类工厂以手动对远程引用进行引用计数不会有帮助,因为由proc服务器中的DLL注册的对象将使用DLL类工厂,并尝试在此工厂上使用IExternalConnection。此外,如果服务器在其进程空间中生成交互式子对象,则不会以任何方式触发IExternalConnection

也不可能修改DLL的引用计数以使用CoAddressServerProcess/CoReleaseServerProcess,因为DLL无法访问容器的关闭代码,以防它触发上一版本,并且无论如何都无法更改第三方DLL

我能想到的唯一可行的方法是在服务器refcount达到零后添加一个循环,它调用cofreeunsedlibraries,然后以某种方式枚举所有加载的COM DLL并等待,直到没有加载,并确保服务器refcount仍然为零。如果加载的DLL没有正确地实现CanUnloadNow,这将导致进程泄漏,并涉及到我希望避免的混淆低级COM实现细节

有没有更简单的方法来确保由进程内服务器的类工厂实例化的COM对象保持进程的活动状态,或者枚举加载到当前进程中的DLL的类工厂并查询它们的引用数


更新:另一种可能有效的方法,但听起来很像你不应该做的事情:截取过程中产生的每个线程,通过CoRegisterInitializeSpy注册一个CoInitialize钩子,并为当前已初始化COM的每个线程添加服务器进程引用。

进程外EXE可以委托DLL对象,而不是直接返回它。让
GetResults()
返回EXE实现的对象,并让该实现根据需要在内部使用DLL。这样,直到调用方释放EXE对象,EXE才会被释放,从而保持EXE自身的refcount处于活动状态。EXE对象可以实现DLL对象实现的相同接口。这样,调用者就不知道(或不关心)正在使用委托。

只有当有一种通用的方法来拦截对子对象的所有可能调用,从而可以创建/返回更多的子对象时,这种方法才会起作用,否则必须进行包装的对象树将无限增长,运行时定义的IDispatch方法也可以转义。这还需要将所有包装映射到真实对象以处理反向引用。必须对整个树进行包装,以便维护EXE自己的refcount。+1并且可以通过在根对象上实现
IMarshal
来自动包装(尽管这是一项繁琐的任务)。正确的术语是“bug”。如果无法修复服务器,则最好保留一个接口引用。请与服务器的作者或供应商联系以获取建议。@HansPassant:我可以修复服务器,它目前正在使用CAtlExeModuleT管理其生存期。但是,只要通过自己的类工厂创建的COM对象的实例存在,该类就只能使服务器保持活动状态。我无法理解如何扩展锁定/解锁行为,以同时尊重DLL在DLLCanUnloadNow中使用的通过CoGetClassObject/CoCreateInstance动态加载的DLL的私有引用计数。