在处理进程间COM对象时,在不使用QueryInterface的情况下将IDispatch*强制转换为IUnknown*是否安全?
在这里,我们的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作为参数。
但是,通过在IDispatch上调用IDispatch,就像我的同事建议我这样做的那样,我很难回到我试图解决的同一个问题中:这个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;发布于 2015-06-10 18:06:39
将IDispatch转换为C++中的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调用来检查服务器是否可用可能是一项昂贵的操作,这取决于您的场景。
发布于 2015-06-10 14:44:33
为了检测对象是否是远程的,CoIsHandlerConnected无论如何都会QueryInterface参数(对于IProxyManager等),所以不管您是提供已经拥有的指针,还是另外查询IUnknown。您的QueryInterface调用对远程对象的状态没有影响:该对象是否远程,远程对象是否已死- CoIsHandlerConnected对您有相同的结果,而不管您的附加QueryInterface如何。因此,没有必要这样做。
然后,另一个注意事项是,如果远程对象死了(进程外服务器崩溃等),调用IDispatch::Invoke仍然是安全的。代理只返回错误代码,而不存在未定义的行为。也就是说,看起来您根本不需要CoIsHandlerConnected,如果您在客户端进程的上下文中遇到了访问冲突,那么您可能首先需要解决其他问题。
发布于 2015-06-10 14:05:49
不,你应该一直QueryInterface。
仅仅因为您已经建立了一个IUnknown接口,这并不意味着您可以直接将它转换为IDispatch。COM可能为基础对象提供了一个代理,这意味着指针与IDispatch无关。
类似地,实现可以包装一个实现IDispatch的对象,当您调用QueryInterface时,它将委托给这个对象。或者您可能有一个指向委托给外部IUnknown的COM对象的指针。
所以,基本上,永远不要直接投,即使你认为它会起作用,因为事情可能会随着时间的推移而改变。调用QueryInterface很少是性能瓶颈,因此不值得避免。
https://stackoverflow.com/questions/30758238
复制相似问题