Поделиться через


Как работает IUnknown

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие API, чтобы по возможности использовать новые API.]

Методы в IUnknown позволяют приложению запрашивать интерфейсы в компоненте и управлять количеством ссылок компонента.

Количество ссылок

Число ссылок — это внутренняя переменная, увеличенная в методе AddRef и уменьшенная в методе Release . Базовые классы управляют количеством ссылок и синхронизируют доступ к счетчику ссылок между несколькими потоками.

Запросы интерфейса

Запрос к интерфейсу также является простым. Вызывающий объект передает два параметра: идентификатор интерфейса (IID) и адрес указателя. Если компонент поддерживает запрошенный интерфейс, он задает указатель на интерфейс, увеличивает собственное число ссылок и возвращает S_OK. В противном случае он задает для указателя значение NULL и возвращает E_NOINTERFACE. В следующем псевдокоде показан общий контур метода QueryInterface . Агрегат компонентов, описанный в следующем разделе, представляет дополнительную сложность.

if (IID == IID_IUnknown)
    set pointer to (IUnknown *)this
    AddRef
    return S_OK

else if (IID == IID_ISomeInterface)
    set pointer to (ISomeInterface *)this
    AddRef
    return S_OK

else if ... 

else
    set pointer to NULL
    return E_NOINTERFACE

Единственное различие между методом QueryInterface одного компонента и методом QueryInterface другого заключается в списке идентификаторов IID, которые тестируются каждым компонентом. Для каждого интерфейса, который поддерживает компонент, компонент должен проверить наличие IID этого интерфейса.

Агрегирование и делегирование

Агрегирование компонентов должно быть прозрачным для вызывающей стороны. Таким образом, агрегат должен предоставлять один интерфейс IUnknown , при этом агрегированный компонент откладывается на реализацию внешнего компонента. В противном случае вызывающий объект увидит два разных интерфейса IUnknown в одном агрегате. Если компонент не является агрегированным, он использует собственную реализацию.

Для поддержки этого поведения компонент должен добавить уровень косвенного обращения. Делегирование IUnknown делегирует работу в соответствующее место: внешнему компоненту, если он есть, или во внутреннюю версию компонента. Неразделяющий IUnknown выполняет эту работу, как описано в предыдущем разделе.

Делегированная версия является общедоступной и сохраняет имя IUnknown. Неотделяющая версия переименована в INonDelegatingUnknown. Это имя не является частью спецификации COM, так как оно не является общедоступным интерфейсом.

Когда клиент создает экземпляр компонента, он вызывает метод IClassFactory::CreateInstance . Один из параметров является указателем на интерфейс IUnknown компонента агрегирования или значение NULL , если новый экземпляр не является агрегированным. Компонент использует этот параметр для хранения переменной-члена, указывающей, какой интерфейс IUnknown использовать, как показано в следующем примере:

CMyComponent::CMyComponent(IUnknown *pOuterUnkown)
{
    if (pOuterUnknown == NULL)
        m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;
    else
        m_pUnknown = pOuterUnknown;

    [ ... more constructor code ... ]
}

Каждый метод в делегировании IUnknown вызывает его неделегирующие аналоги, как показано в следующем примере:

HRESULT QueryInterface(REFIID iid, void **ppv) 
{
    return m_pUnknown->QueryInterface(iid, ppv);
}

По характеру делегирования методы делегирования выглядят идентичными в каждом компоненте. Изменяются только неразделяющие версии.

Реализация IUnknown