Безопасно ли преобразовывать IDispatch * в IUnknown * без использования QueryInterface для межпроцессных COM-объектов?

При работе с межпроцессным 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;

1

Решение

Кастинг 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 для проверки доступности сервера может быть дорогостоящей операцией, в зависимости от вашего сценария.

2

Другие решения

Чтобы определить, является ли объект удаленным CoIsHandlerConnected было бы QueryInterface аргумент в любом случае (для IProxyManager и т. д.), поэтому не имеет значения, предоставляете ли вы уже имеющийся указатель или дополнительно запрашиваете IUnknown, Ваш QueryInterface вызов не влияет на состояние удаленного объекта: удаленный объект или нет, удаленный объект мертв или нет — CoIsHandlerConnected имеет одинаковый результат для вас, независимо от вашего дополнительного QueryInterface, Следовательно, нет необходимости делать это.

Еще одно замечание: звонить по-прежнему безопасно. IDispatch::Invoke если удаленный объект мертв (сбой сервера вне процесса и т. д.). Прокси просто возвращает код ошибки без неопределенного поведения. То есть, похоже, тебе не нужно CoIsHandlerConnected и вообще, если вы столкнулись с нарушениями доступа в контексте клиентского процесса, то вам, вероятно, придется сначала решить другие проблемы.

2

Нет, ты всегда должен QueryInterface,

Просто потому, что вы ком IUnknown интерфейс это не значит, что вы можете напрямую привести его к IDispatch, COM, возможно, дал вам прокси для базового объекта, что означает, что указатель не имеет ничего общего с IDispatch,

Аналогично, реализация может обернуть объект, который реализует IDispatch и когда вы звоните QueryInterface он делегирует этому объекту. Или у вас может быть указатель на COM-объекты, который делегирует внешнему IUnknown,

Так что, по сути, никогда не применяйте напрямую, даже если думаете, что это сработает, потому что со временем все может измениться. призвание QueryInterface редко является узким местом с точки зрения производительности, и поэтому его не стоит избегать.

1