在处理进程间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崩溃或被杀死的情况(访问冲突)。似乎在Invoke上调用诸如IDispatch之类的函数来封装此对象,而不再存在OtherProcess.exe会引发这些访问冲突(编辑:注释和答案表明此最新假设完全是错误的!)。

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

但是,就像我的同事建议的那样,通过在QueryInterface上调用IDispatch,我恐怕又陷入了我要解决的相同问题:此IDispatch处理不再存在的对象,而将QueryInterface转换为IUnknown只是未定义的行为都相同(再次 EDIT ,这个假设也是错误的)。

我刚做 Actor 时真的错了吗?
处理失效的进程间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;

最佳答案

在C++中将IDispatch转换为IUnknown(类似于static_cast<IUnknown*>(pDispatch))会产生完全相同的指针值,因为IDispatch源自IUnknown。 OTOH,对QueryInterface上的IID_IUnknown执行pDispatch可能会返回不同的指针,但这仍然是合法操作。实际上,这就是获取COM对象身份的方法,例如,检查两个接口(interface)是否由同一个COM对象实现(硬COM规则始终在同一个COM单元内工作)。

就是说,由COM编码器实现的代理COM对象可能是缓存接口(interface),因此,尽管远程服务器已经关闭,对IDispatch::QueryInterface的调用仍可能返回S_OK和代理的有效IUnknown身份。也就是说,这种操作可能不会引起即时IPC call 。

在您的情况下,要测试COM服务器是否仍然正常运行,我只需要对已经拥有的代理对象调用IDispatch::GetTypeInfoCount。实际上,这将导致IPC call (如果服务器在其他主机上运行,​​则可能导致有线往返)。

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

但是请注意,根据您的方案,仅检查服务器是否可用可能需要执行额外的IPC调用,这可能是一项昂贵的操作。

09-06 18:45