将IDispatch *强制转换为IUnknown *而不使用QueryInterface来处理进程间COM对象是否安全?

时间:2015-06-10 13:46:43

标签: c++ com atl dcom dangling-pointer

在处理进程间COM个对象时,将IDispatch*转换为IUnknown*是否安全,而不使用QueryInterface

此处我们的IDispatch对象来自其他进程OtherProcess.exe。 我的一位同事说我应该在QueryInterface上致电IDispatch以获得IUnknown

目前我在做:

void CComThrowDispatch::CheckCOMAvailabilty() const
{
    IUnknown * pIUnknown = m_spDispatchDriver.p;   
    // is this line above a problem ? 
    // m_spDispatchDriver is an ATL CComDispatchDriver 
    // it handles an object instanciated in another process.
    // m_spDispatchDriver.p is of type IDispatch*

    if (pIUnknown == nullptr) return;
    bool bComObjectReachable = ::CoIsHandlerConnected(pIUnknown) == TRUE;
    if (bComObjectReachable == false)
    {
        throw MyException;
    }
}

我的问题是他的建议:当OtherProcess.exe崩溃或被杀死时,我正在处理案例(访问冲突)。它似乎调用了InvokeIDispatch之类的任何函数,它们封装了不再存在的任何对象.OtherProcess.exe会引发这些访问冲突(编辑:评论和答案显示这一点最新的假设是完全错误的!)。

这就是我尝试保护以::CoIsHandlerConnected(pIUnknown);为参数的应用程序测试IUnknown的原因。

但是,通过致电QueryInterface上的IDispatch,就像我的同事建议我做的那样,我害怕回到我想要解决的同一个问题:这个IDispatch处理一个不再存在的对象,QueryInterfaceIUnknown只会是未定义的行为(编辑再次,这个假设也是假的)。

当我做演员表演时我真的错了吗? 处理死时进程间COM对象的常用方法是什么?

这是OAIdl.h中IDispatch定义的开头,它被声明为派生自IUnknown

MIDL_INTERFACE("00020400-0000-0000-C000-000000000046")
IDispatch : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( 
        /* [out] */ __RPC__out UINT *pctinfo) = 0;

3 个答案:

答案 0 :(得分:2)

为了检测对象是否是远程CoIsHandlerConnected无论如何QueryInterface参数(对于IProxyManager等),所以无论你是否提供已有的指针都没关系,或者你另外查询IUnknown。您的QueryInterface调用对远程对象的状态没有影响:对象是否是远程对象,远程对象是否已经死亡 - CoIsHandlerConnected对您有相同的结果,无论您是多少{ {1}}。因此,没有必要这样做。

然后另一个注意事项是,如果远程对象死了(进程外服务器崩溃等),则调用QueryInterface仍然是安全的。代理只返回错误代码而没有未定义的行为。也就是说,看起来您根本不需要IDispatch::Invoke,如果您在客户端进程的上下文中遇到访问冲突,那么您可能首先要解决其他问题。

答案 1 :(得分:2)

在C ++中将IDispatch转换为IUnknown(如static_cast<IUnknown*>(pDispatch))会产生完全相同的指针值,因为IDispatch派生自IUnknown。 OTOH,对QueryInterface IID_IUnknown执行pDispatch可能会返回不同的指针,但它仍然是合法的操作。实际上,这是如何获取COM对象的标识,比如检查两个接口是否由同一个COM对象实现(一个硬COM规则总是在同一个COM公寓内工作)。

也就是说,COM编组器实现的代理COM对象可以缓存接口,因此对IDispatch::QueryInterface的调用可能会返回S_OK和有效IUnknown尽管远程服务器已经关闭,但代理的身份仍然存在。也就是说,此类操作可能不会导致即时IPC调用。

在您的情况下,为了测试COM服务器是否还活着,我只需在您已有的代理对象上调用IDispatch::GetTypeInfoCount。这实际上会导致IPC调用(或者如果服务器在不同的主机上运行则通过线路往返)。

如果远程服务器崩溃或不可用,您可能会收到CO_E_OBJNOTCONNECTED错误(可能是不同的错误代码,但肯定不是S_OK)。

请注意,根据您的情况,执行额外的IPC调用以检查服务器是否可用可能是一项代价高昂的操作。

答案 2 :(得分:1)

不,你应该总是rr-@burza:~$ cat /proc/29262/status | grep -i rss VmRSS: 1736 kB rr-@burza:~$ cat /proc/29262/status | grep -i vmsize VmSize: 5980 kB

仅仅因为您使用了QueryInterface界面,它并不代表您可以直接将其投放到IUnknown。 COM可能已经为您提供了底层对象的代理,这意味着指针与IDispatch无关。

同样,实现可以包装实现IDispatch的对象,当您调用IDispatch时,它会委托给该对象。或者,您可能有一个指向委托给外部QueryInterface的COM对象的指针。

所以,基本上,即使你认为它会起作用,也绝不会直接投射,因为事情可能会随着时间的推移而改变。调用IUnknown很少是性能瓶颈,因此不值得避免。