Обязательно ли агрегированные объекты должны быть ссылкой IUnknown?

Я пытаюсь реализовать общую логику, используя агрегацию COM с ATL. Я определил базовый класс с именем CameraBase, который доступен только через агрегацию. Поэтому я добавил аннотацию aggregateable к его coclass-объявлению.

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

Я также добавил макрос 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()

    ...
}

Теперь у меня есть разные классы, которые где-то используют логику CameraBase. Поэтому я расширил карту com родительского класса (например, SampleCamera):

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

DECLARE_GET_CONTROLLING_UNKNOWN()

Поскольку я хочу иметь возможность вызывать членов CameraBase (через интерфейс ICamera) из родительского класса, я не хочу использовать COM_INTERFACE_ENTRY_AUTOAGGREGATE, который хранит указатель внутреннего объекта как ссылку на IUnknown. Поэтому я создаю его самостоятельно из FinalConstruct-метода:

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). Мой текущий обходной путь состоит в том, чтобы сохранить две ссылки, IUnknown и ICamera, и запросить более позднюю.

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

Это работает, но выглядит немного странно, так как экземпляр класса (CameraBase), для которого создается экземпляр, одинаков в обоих случаях. Я что-то упускаю? Правильно ли я использую внутренний объект? Почему возвращаемый указатель CoCreateInstance должен иметь тип IUnknown, если передается внешний неизвестный?

Заранее спасибо! :)


person Carsten    schedule 12.01.2017    source источник


Ответы (1)


Агрегируемый COM-объект предоставляет две различные реализации IUnknown — без делегирования и с делегированием.

Реализация без делегирования является "нормальной" - ее QueryInterface передает интерфейсы, реализованные агрегируемым объектом, а ее AddRef и Release контролируют время жизни этого объекта.

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

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

person Igor Tandetnik    schedule 12.01.2017
comment
Спасибо за подробный ответ! :) Итак, насколько я понял, я не сделал ничего плохого, и сохранение неделегирующего указателя IUnknown внутреннего объекта является требованием соглашения об агрегации COM. - person Carsten; 12.01.2017