IUnknown 的工作原理

[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayerIMFMediaEngine媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 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