对于进程间COM对象,不使用QueryInterface,将IDispatch*转换为IUnknown*是否安全?
Is it safe to cast a IDispatch* into an IUnknown*, without using QueryInterface, for interprocess COM objects?
在处理进程间的COM
对象时,不使用QueryInterface
而将IDispatch*
转换为IUnknown*
是否安全?
这里的IDispatch
对象来自另一个进程OtherProcess.exe
。我的一个同事说我应该在IDispatch
上调用QueryInterface
,以便获得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崩溃或已被杀死时,我正在处理的情况(访问违规)。似乎在IDispatch
上调用Invoke
这样的函数封装了不再存在的OtherProcess.exe中的任何对象,会引发这些访问违规(EDIT:注释和答案揭示了这个最新的假设是完全错误的!)。
这就是为什么我试图保护应用程序测试::CoIsHandlerConnected(pIUnknown);
,它以IUnknown
为参数。
但是通过在IDispatch
上调用QueryInterface
,就像我的同事建议我做的那样,我害怕回到我试图解决的同一个问题:这个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;
在c++中将IDispatch
转换为IUnknown
(与static_cast<IUnknown*>(pDispatch)
一样)会产生完全相同的指针值,因为IDispatch
派生自IUnknown
。OTOH,在pDispatch
上为IID_IUnknown
做QueryInterface
可能会返回不同的指针,但它仍然是一个合法的操作。事实上,这就是如何获得一个COM对象的标识,比如说,检查两个接口是否由同一个COM对象实现(一个总是在同一个COM公寓内工作的硬COM规则)。
IDispatch::QueryInterface
的调用可能返回S_OK
和代理的有效IUnknown
标识,尽管远程服务器已经关闭。也就是说,这种操作可能不会引起即时IPC调用。
在您的例子中,要测试COM服务器是否仍然存活并且运行良好,我只需在您已经拥有的代理对象上调用IDispatch::GetTypeInfoCount
。这实际上会导致IPC调用(如果服务器运行在不同的主机上,则会通过线路进行往返)。
如果远程服务器崩溃或不可用,您可能会收到CO_E_OBJNOTCONNECTED
错误(可能是不同的错误代码,但肯定不是S_OK
)。
请注意,根据您的场景,执行额外的IPC调用只是为了检查服务器是否可用可能是一个昂贵的操作。
不,你应该总是使用QueryInterface
。
仅仅因为你已经创建了一个IUnknown
接口,并不意味着你可以直接将它强制转换为IDispatch
。COM可能给了你一个底层对象的代理,这意味着指针与IDispatch
无关。
同样,一个实现可以包装一个实现IDispatch
的对象,当你调用QueryInterface
时,它委托给这个对象。或者你可以有一个指向COM对象的指针,该对象委托给外部IUnknown
。
QueryInterface
很少会成为性能瓶颈,因此不值得避免。
为了检测对象是否远程CoIsHandlerConnected
将QueryInterface
的参数(对于IProxyManager
等),所以它并不重要你是否提供你已经拥有的指针,或者你额外查询IUnknown
。你的QueryInterface
调用对远程对象的状态没有影响:无论对象是否远程,无论远程对象是否死亡- CoIsHandlerConnected
对你有相同的结果,不管你额外的QueryInterface
。因此,没有必要做这件事。
然后另一个注意事项是,如果远程对象已死(进程外服务器崩溃等),调用IDispatch::Invoke
仍然是安全的。代理只是返回没有未定义行为的错误代码。也就是说,看起来您根本不需要CoIsHandlerConnected
,如果您在客户端进程上下文中遇到访问违规,那么您可能有其他问题需要首先解决。
- "IDispatch::GetTypeInfo()"接口是否正常
- 如何在qt中修复"No query Unable to fetch row"
- ATL COM - EnumWindows 回调函数不适用于 IDispatch::Invoke
- 使用 IDispatch::调用进行 OLE 交互的参数构造
- Qt应用程序中的"No query Unable to fetch row"
- query CL_DEVICE_MAX_WORK_ITEM_SIZES
- 在导入 Foundation 的 objective-c 文件上运行 Clang-query
- MySQL 异常"connection lost during query"、"MySQL server has gone away"和"command out of sync"
- qt-running query
- 处理IDISPATCH参数和COM
- 为什么 QSqlQuery query.next() 返回 false
- QT:在变量中保存SQL-Query的结果,在SQL Query中使用C 变量
- 调用C++中JScript中的instanceof运算符(IDispatch/IDispatchEx)
- 正在将QAxObject事件与IDispatch*类型的参数连接
- 如何在 webkit 浏览器控件中返回指向 JavaScript 的 IDispatch 指针
- QT query.exec() 总是返回 false
- IDispatch returns DISP_E_UNKNOWNNAME for CommandBarButton.St
- 封送C#类型以调用C++IDispatch接口会导致类型不匹配
- IDispatch::Invoke(DISPATCH_PROPERTYGET)是否增加返回的IDispatch接口的引用
- 接口不能从非接口的IDispatch继承