Выпуск коллекции COM-интерфейсов с помощью вызова IUnknown::Release()

#c #com #com-interface

Вопрос:

В моем приложении мне нужно отложить выпуск COM-интерфейсов до более позднего момента. Для этого я сохраняю указатели интерфейса std::vector<IUnknown*> COMInterfaces , а затем перебираю все указатели и вызываю Release() вот так:

 for(IUnknown* item : COMInterfaces) item->Release();
 

Однако по следующей ссылке, в разделе 4.1.3, я прочитал:

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

Итак, теперь я немного запутался, можно ли выпускать интерфейсы полиморфно или нет. Я не могу найти никакой документации, в которой четко указано, нормально это или нет.

Редактировать: Комментарии ниже подтверждают, что это работает, и я собираюсь использовать его таким образом. Тем не менее, любые указания на официальную документацию будут приветствоваться, а также любые объяснения того, почему иногда я вижу следующий код (это также SafeRelease() определяется Microsoft)

 template<class T>
void Release(T*amp; comInterface) {
    if(comInterface) {
      comInterface->Release();
      comInterface = nullptr;
    }
}
 

вместо

 void Release(IUnknown*amp; comInterface) {
    if(comInterface) {
      comInterface->Release();
      comInterface = nullptr;
    }
}
 

Комментарии:

1. Чего вы не можете сделать, так это привести IUnknown* к чему-либо другому (вы всегда должны использовать QueryInterface, а не «сырое» приведение). Если вы этого не сделаете, то не должно быть никаких проблем.

2. Любой COM — интерфейс является неизвестным. Любой брошенный или брошенный, пока вы только звоните AddRef() Release() , и QueryInterface() должен быть в безопасности. Допустим, у вас есть IDispatch интерфейс. Ты звонишь lpDisp->Release() . Это дало бы тот же результат, если бы вы позвонили ((IUnknown*) lpDisp)->Release() .

3. Я считаю, что этот параграф конкретно означает интерфейсы, которые можно найти только с QueryInterface помощью (а не путем понижения). В этот момент вступает в действие алмазное наследование, и каждый запрашиваемый интерфейс имеет свою собственную копию IUnknown . Эти копии могут иметь различные реализации трех методов, о чем, вероятно, предупреждает этот параграф

4. Кстати, было бы безопаснее использовать std::vector<CComPtr<IUnknown>> вместо этого, тогда вам не придется звонить Release() вручную. Таким образом, интерфейсы освобождаются автоматически, когда vector они выходят за рамки области действия, особенно если возникает неожиданное исключение.

5. Ваша версия Release не может быть вызвана с помощью какого-либо другого типа указателя, кроме IUnknown* конкретного. Это не связано с COM, просто базовая безопасность типов. Если бы вы могли перейти Dervied* p к взятию функции Base*amp; , эта функция могла бы назначить какую-то не связанную AnotherDerived* с p ней .