1

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

这里我们的IDispatch对象来自另一个进程OtherProcess.exe。我的一位同事说我应该打电话QueryInterface给.IDispatchIUnknown

目前我正在做:

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 崩溃或被杀死的情况(访问冲突)。似乎调用任何函数,如封装来自不再存在的 OtherProcess.exeInvoke的任何对象的任何函数都会引发这些访问冲突(编辑:评论和答案表明这个最新的假设是完全错误的!)。IDispatch

这就是我试图保护::CoIsHandlerConnected(pIUnknown);采用IUnknownas 参数的应用程序测试的原因。

但是通过调用QueryInterfaceIDispatch就像我的同事建议我做的那样,我害怕退回到我试图解决的同一个问题:这IDispatch处理一个不再存在的对象,并且QueryInterface对于一个 IUnknown将只是未定义的行为都一样(再次编辑,这个假设也是错误的)。

当我只是做演员时,我真的错了吗?处理死进程间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;
4

3 回答 3

2

为了检测对象是否是远程的,无论如何CoIsHandlerConnected都会QueryInterface使用参数(对于IProxyManager等),因此无论您提供已有的指针,还是额外查询IUnknown. 您的QueryInterface调用对远程对象的状态没有影响:对象是否为远程,远程对象是否已死 -CoIsHandlerConnected无论您是否添加了QueryInterface. 因此,没有必要这样做。

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

于 2015-06-10T14:44:33.357 回答
2

在 C++ 中转换为(如)IDispatch产生完全相同的指针值,因为派生自. OTOH,做for on可能会返回一个不同的指针,但它仍然是一个合法的操作。事实上,这就是如何获取 COM 对象的身份,例如,检查两个接口是否由同一个 COM 对象实现(一个始终在同一个 COM 单元内工作的硬 COM 规则)。IUnknownstatic_cast<IUnknown*>(pDispatch)IDispatchIUnknownQueryInterfaceIID_IUnknownpDispatch

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

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

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

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

于 2015-06-10T18:06:39.503 回答
1

不,你应该永远是你QueryInterface

仅仅因为你有一个IUnknown接口并不意味着你可以直接将它转换为IDispatch. COM 可能已经为您提供了底层对象的代理,这意味着该指针与IDispatch.

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

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

于 2015-06-10T14:05:49.387 回答