如何从进程外的COM服务器返回接口指针的正确方法?

3

我有一个out-of-proc COM服务器,其中有两个ATL COM对象。object1公开了一个接口,该接口在内部创建object2,并以以下方式返回其接口指针:

HRESULT CObject1::CreateObject2(IObject2** pIobj2)
{
    CComObject<Object2>* pObj = NULL;

    HRESULT hr = CComObject<Object2>::CreateInstance(&pObj);

    hr = pObj->QueryInterface(IID_IObject2,(void**)pIobj2);

    //Some reason i need to store this pIobj2
    (*pIobj2)->AddRef();

    return hr;
}

Object1暴露了另一个接口来移除Object2。

HRESULT CObject1::RemoveObject2(IObject2* pIobj2)
{
    pIobj2->Release(); //This to compensate QI done in CreateObject2

    pIobj2->Release(); //This to compensate addref done to store in create
}

我的要求是客户端在IObject1::CreateObject2()之后不得在IObject2指针上调用Release。要销毁Object2,应该调用IObject1::RemoveObject2()。当我执行客户端时,在RemoveObject2之后,Object2并未被销毁,但是当我在客户端上在RemoveObject2之后调用IObject2指针的Release时,Object2被销毁了。


这样不行。客户端仍然有一个非零引用计数的代理。除了通过事件通知客户端调用Release()之外,您无法以可靠的方式使其工作。 - Hans Passant
@HansPassant:感谢您的回复。换句话说,客户端的引用计数与服务器端存在的引用计数不同,客户端必须始终调用释放,无论服务器是否管理其自己的引用计数。如果我的理解正确,请告诉我。再次感谢您。 - Shank
1个回答

2
在进程外的COM对象中,生命周期管理比进程内的复杂得多。例如,系统必须考虑到另一方可能会死亡(例如,如果客户端进程在没有释放的情况下死亡,则服务器应清理为对象分配的资源)。为此,Microsoft提供了DCOM垃圾回收。
确实有两个COM对象,一个在客户端(代理)上,一个在服务器(存根)上。代理维护自己的引用计数,您的代码混淆了这个引用计数,因为您在服务器上释放了对象2的引用计数 - 这些信息将不会传递到客户端。
我强烈建议您更改架构。首先,COM合同是释放对象的方法是使用Release方法。这是合同,而您的设计更改了它,这就是为什么您会让DCOM感到困惑的原因。
我认为更好的设计是:
1. 在Object2中保留对Object1的引用(例如,m_pobj1) 2. 不要有Object1 :: RemoveObject2。相反,如果需要执行某些操作,请使用一个私有(非COM)CleanupObject2。 3. 实现Object2 :: FinalRelease(Object2的清理方法),并调用m_pobj1->Cleanup [Object2(this)] 4. 请记住,如果您需要在obj1中保留对obj2的指针,请确保这些是弱引用。也就是说,仅是指针 - 不添加引用计数。
客户端调用Object1 :: CreateObject2并获取对obj2的引用。完成后,他们将调用obj2->Release(),清除引用计数,并调用obj1->CleanupObject2方法。

非常感谢。这有助于我更好地理解COM。 - Shank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接