2015-06-10 5 views
1

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

Здесь наш объект 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, вызывает эти нарушения доступа (EDIT: комментарии и ответы показывают, что это последнее предположение было полностью ложным!).

Именно поэтому я пытаюсь защитить тестирование приложения ::CoIsHandlerConnected(pIUnknown);, которое принимает параметр IUnknown.

Но по телефону QueryInterface на IDispatch, как мой коллега советует мне делать, я боюсь упасть обратно в той же проблеме, я пытаюсь решить: Этот IDispatch обрабатывает объект, который больше не существует, и QueryInterface к IUnknown будет просто неопределенным Поведение все равно (EDIT опять же это предположение также неверно).

Я действительно ошибаюсь, когда я просто делаю актеры? Каков общий способ борьбы с мертвым межпроцессом 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; 
+2

* На самом деле * неправильно использовать. Вызов метода на интерфейсе с мертвого сервера вызывает ошибку RPC, а не AVE. Вам нужно получить RPC_E_SERVERDIED. Вам нужно сосредоточиться на реальной проблеме здесь, прямо сейчас вы просто делаете это хуже. –

+0

@ HansPassant Это интересно, спасибо. Однако мне трудно понять, почему это * действительно неправильно * для ** upcast ** IDispatch, который происходит из IUnknown, в IUnknown. Не могли бы вы рассказать об этом? Какова первоначальная ошибка, которую я делаю? –

+2

@StephaneRolland: В некоторых случаях вам нужно/нужно запрашивать «IUnknown», даже если какой-либо интерфейс получен из него и доступен ли он доступен (см. Раздел [Объекты должны иметь идентификаторы] »(https://msdn.microsoft.com/en-us /library/windows/desktop/ms686590%28v=vs.85%29.aspx)), однако это не так в вашем вопросе. –

ответ

1

IDispatch Casting к IUnknown в C++ (как static_cast<IUnknown*>(pDispatch)) дает точно такое же значение указателя, потому что IDispatch происходит от IUnknown. OTOH, делая QueryInterface для IID_IUnknown по адресу pDispatchmay возвращает другой указатель, но это все еще законная операция. Фактически, это, как получить идентификатор COM-объекта, скажем, чтобы проверить, реализуются ли два интерфейса одним и тем же объектом COM (жесткое правило COM, которое всегда работает внутри одной и той же квартиры COM).

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

В вашем случае, чтобы проверить, что COM-сервер все еще жив и здоров, я просто позвоню IDispatch::GetTypeInfoCount на прокси-объект, который у вас уже есть. Это фактически вызовет вызов IPC (или кругооборот по проводу, если сервер работает на другом хосте).

Если удаленный сервер поврежден или недоступен, вероятно, вы получите ошибку CO_E_OBJNOTCONNECTED (возможно, это может быть другой код ошибки, но, конечно, не S_OK).

Обратите внимание, что выполнение дополнительного вызова IPC только для проверки доступности сервера может быть дорогостоящей операцией, в зависимости от вашего сценария.

1

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

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

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

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

+0

У меня есть IDispatch *, и я передал его в IUnkown *. ** IDispatch происходит от IUnkown **. Вы отвечаете на разговоры о другом, о чем я полностью согласен, это неправильно. ** Даунгаун опасен **, но не догадываюсь, насколько я знаю. Не так ли? –

+0

@StephaneRolland - это все еще не безопасно. Ваш 'IDispatch' может быть членом общего объекта. С помощью downcasting вы увидите «IUnknown» члена, а не контрольный «IUnknown», который фактически представляет COM-объект. – Sean

2

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

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