При работе с межпроцессным 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
будет просто неопределенным поведением все равно (РЕДАКТИРОВАТЬ опять же, это предположение также неверно).
Я действительно не прав, когда просто снимаюсь?
Каков общий способ борьбы с мертвым межпроцессным процессом? COM
объекты ?
Это начало определения IDispatch
в OAIdl.h, который объявлен как происходящий из IUnknown
,
MIDL_INTERFACE("00020400-0000-0000-C000-000000000046")
IDispatch : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo) = 0;
Кастинг IDispatch
в IUnknown
в C ++ (как 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 для проверки доступности сервера может быть дорогостоящей операцией, в зависимости от вашего сценария.
Чтобы определить, является ли объект удаленным CoIsHandlerConnected
было бы QueryInterface
аргумент в любом случае (для IProxyManager
и т. д.), поэтому не имеет значения, предоставляете ли вы уже имеющийся указатель или дополнительно запрашиваете IUnknown
, Ваш QueryInterface
вызов не влияет на состояние удаленного объекта: удаленный объект или нет, удаленный объект мертв или нет — CoIsHandlerConnected
имеет одинаковый результат для вас, независимо от вашего дополнительного QueryInterface
, Следовательно, нет необходимости делать это.
Еще одно замечание: звонить по-прежнему безопасно. IDispatch::Invoke
если удаленный объект мертв (сбой сервера вне процесса и т. д.). Прокси просто возвращает код ошибки без неопределенного поведения. То есть, похоже, тебе не нужно CoIsHandlerConnected
и вообще, если вы столкнулись с нарушениями доступа в контексте клиентского процесса, то вам, вероятно, придется сначала решить другие проблемы.
Нет, ты всегда должен QueryInterface
,
Просто потому, что вы ком IUnknown
интерфейс это не значит, что вы можете напрямую привести его к IDispatch
, COM, возможно, дал вам прокси для базового объекта, что означает, что указатель не имеет ничего общего с IDispatch
,
Аналогично, реализация может обернуть объект, который реализует IDispatch
и когда вы звоните QueryInterface
он делегирует этому объекту. Или у вас может быть указатель на COM-объекты, который делегирует внешнему IUnknown
,
Так что, по сути, никогда не применяйте напрямую, даже если думаете, что это сработает, потому что со временем все может измениться. призвание QueryInterface
редко является узким местом с точки зрения производительности, и поэтому его не стоит избегать.