聚合对象是否强制成为 IUnknown 引用?

Are aggregated objects forced to be an IUnknown reference?

我正在尝试使用带有 ATL 的 COM 聚合来实现共享逻辑。我定义了一个 base class,称为 CameraBase,它只能通过聚合获得。因此,我在它的 coclass 声明中添加了 aggregateable 注释。

[
    uuid(...),
    aggregatable
]
coclass CameraBase
{
    [default] interface ICamera;
};

我还在 class 定义中添加了 DECLARE_ONLY_AGGREGATEABLE 宏。

class ATL_NO_VTABLE CCameraBase :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CCameraBase, &CLSID_CameraBase>,
    public ISupportErrorInfo,
    public IProvideClassInfoImpl<...>,
    public IDispatchImpl<...>
{
public:
    CCameraBase()
    {
    }

    DECLARE_REGISTRY_RESOURCEID(IDR_CAMERABASE)

    DECLARE_ONLY_AGGREGATABLE(CCameraBase)

    BEGIN_COM_MAP(CCameraBase)
        ...
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    ...
}

现在我有不同的 classes 在某处使用 CameraBase 的逻辑。因此,我扩展了父 class 的 com 映射(例如 SampleCamera):

BEGIN_COM_MAP(CSampleCamera)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_ICamera, m_base)
    ...
END_COM_MAP

DECLARE_GET_CONTROLLING_UNKNOWN()

由于我希望能够从父 class 调用 CameraBase 上的成员(通过 ICamera 接口),我不想使用 COM_INTERFACE_ENTRY_AUTOAGGREGATE,它将内部对象的指针存储为 IUnknown 的引用。因此,我使用 FinalConstruct-method:

自行创建它
HRESULT FinalConstruct()
{
    HRESULT hr;

    if (FAILED(hr = m_camera.CoCreateInstance(CLSID_CameraBase, this->GetControllingUnknown(), CLSCTX_INPROC_SERVER)))
        return hr;
}

其中 m_camera 定义为 CComPtr<ICamera>。但是,这确实会导致错误 CLASS_E_NOAGGREGATION (HRESULT 0x80040110)。我目前的解决方法是存储两个引用,IUnknownICamera,并查询后一个。

if (FAILED(hr = m_base.CoCreateInstance(CLSID_CameraBase, this->GetControllingUnknown(), CLSCTX_INPROC_SERVER)) ||
    FAILED(hr = m_base->QueryInterface(&m_camera)))
    return hr;

这行得通,但感觉有点奇怪,因为 get 实例化的 class (CameraBase) 在两种情况下都是相同的。我错过了什么吗?我是否使用正确的方式聚合内部对象?为什么 CoCreateInstance 的返回指针需要是 IUnknown 类型,如果传递了一个外部未知数?

提前致谢! :)

可聚合 COM 对象提供了 IUnknown - non-delegating 和委托的两个不同实现。

non-delegating 实现是 "normal" 实现 - 它的 QueryInterface 分发由可聚合对象实现的接口,以及它的 AddRefRelease 控件该对象的生命周期。

顾名思义,委托实现将所有三个方法调用委托给外部对象的控制 IUnknown。该对象实现的所有其他接口都具有由该委托实现支持的三个 IUnknown 方法。这就是聚合如何为客户端保持它正在处理单个 COM 对象的错觉 - 它允许客户端从外部实现的接口查询到内部实现的接口,并且(更有趣的是)反之亦然。回想一下,IUnknown 的要求是 QueryInterface 实现是对称的和可传递的。

当使用 non-NULL 控制未知参数调用 CoCreateInstance 时,它必须从内部对象请求 IUnknown - 这是外部获得 [=31 的唯一机会=] 实施。您不能在外部接口映射中使用来自内部的任何其他接口指针 - 同样,所有其他接口都由委托未知支持,因此将 QueryInterface 调用转发给它们最终会在外部调用 QueryInterface ,并最终回到界面映射中,导致无限递归。