2017-01-12 16 views
1

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

[ 
    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 где-то. Therefor Я удлинен на ком карту родительского класса (например, 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. Therefor Я создаю это самостоятельно из 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, если передается внешнее неизвестное?

Заранее благодарен! :)

ответ

1

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

Не-делегирование реализации является «нормальными» - его QueryInterface раздает интерфейсы, реализованные агрегируемым объект, и его AddRef и Release контроля срок службы данного объекта.

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

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

+0

Спасибо за подробный ответ! :) Итак, насколько я понял, я ничего не сделал, и сохранение указателя IUnknown внутреннего объекта, не являющегося делегированием, является требованием соглашения об объединении COM. – Carsten