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


TN038. Реализация MFC/OLE IUnknown

Примечание

Следующее техническое примечание не было обновлено, поскольку сначала оно было включено в электронную документацию.В результате некоторые процедуры и разделы могут быть устаревшими или неверными.Для получения последних сведений рекомендуется выполнить поиск интересующей темы в алфавитном указателе документации в Интернете.

Ядром OLE 2 является "компонентно-объектная модель OLE" — модель COM. Модель COM определяет стандарт того, как кооперирующиеся объекты обмениваются данными друг с другом. Сюда входят сведения о том, как выглядит "объект", в том числе как отправляются методы в объекте. Модель COM также определяет базовый класс, от которого наследуются все COM-совместимые классы. Этот базовый класс IUnknown. Хотя интерфейс IUnknown называется классом C++, модель COM не привязана к какому-либо одному языку — ее можно реализовать на C, Паскале или любом другом языке, который может поддерживать двоичную разметку COM-объекта.

OLE относится ко всем производным классам из IUnknown как "интерфейсы". Это важное различие, поскольку "интерфейс", такой как IUnknown, не несет вместе с собой реализации. Он лишь указывает протокол, с помощью которого взаимодействуют объекты, а не конкретные функции этих реализаций. Это разумно для системы, обеспечивающей максимальную гибкость. Задача MFC — реализовать реакцию на событие по умолчанию для программ MFC и C++.

Чтобы разобраться в реализации интерфейса IUnknown в MFC необходимо сначала понять, что представляет собой этот интерфейс. Упрощенная версия IUnknown определена ниже:

class IUnknown
{
public:
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
};

Примечание

Некоторые необходимые подробности соглашения о вызовах, такие как __stdcall, в этой иллюстрации опущены.

Управление памятью управления функций-членов AddRef и Release объекта. В модели COM используется схема подсчета ссылок для отслеживания объектов. На объект никогда не ссылаются непосредственно, как в C++. Вместо этого на COM-объекты всегда ссылаются посредством указателя. Чтобы освободить объект после того как владелец перестал использовать его, вызывается член Release объекта (в отличие от использования оператора delete, как это делалось бы для традиционного объекта C++). Ссылка со счетчиком механизм позволяет для нескольких ссылки на один объект, который требуется управление. Реализация AddRef и Release поддерживает счетчик ссылок в объекте — объект не будет удален до тех пор, пока его счетчик ссылок не достигнет нуля.

AddRef и Выпуск достаточно просты с точки зрения реализации. Вот тривиальная реализация:

ULONG CMyObj::AddRef() 
{ 
    return ++m_dwRef; 
}

ULONG CMyObj::Release() 
{ 
    if (--m_dwRef == 0) 
    {
        delete this; 
        return 0;
    }
    return m_dwRef;
}

Функция-член QueryInterface чуть менее интересна. Не очень интересно иметь объект, единственными функциями-членами которого являются AddRef и Release — было бы неплохо расширить функции объекта помимо предоставляемых свойством IUnknown. Здесь используется QueryInterface. Это позволяет получить другой "интерфейс" в том же самом объекте. Эти интерфейсы обычно являются производными от IUnknown и добавляют дополнительные функции путем добавления новых функций-членов. Интерфейсы модели COM никогда не имеют переменных-членов, объявленных в интерфейсе, и все функции-члены объявляются как чистые виртуальные. Например:

class IPrintInterface : public IUnknown
{
public:
    virtual void PrintObject() = 0;
};

Чтобы получить IPrintInterface при наличии только IUnknown, вызовите QueryInterface с использованием IID интерфейса IPrintInterface. IID — это 128-разрядное число, уникальным образом определяющий интерфейс. Существует IID для каждого интерфейса, определенного пользователем или OLE. Если pUnk — указатель на объект IUnknown, код для извлечения из него IPrintInterface может выглядеть следующим образом:

IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, 
    (void**)&pPrint) == NOERROR)
{
    pPrint->PrintObject();
    pPrint->Release();   
        // release pointer obtained via QueryInterface
}

Это кажется достаточно легкими, но как реализовать объект, поддерживающий IPrintInterface и интерфейс IUnknown? В данном случае это просто, так как IPrintInterface наследуется непосредственно из IUnknown — путем реализации IPrintInterface поддержка IUnknown обеспечивается автоматически. Примеры.

class CPrintObj : public CPrintInterface
{
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULONG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
};

Реализации AddRef и Release будут такими же, как реализованные выше. CPrintObj::QueryInterface может выглядеть следующим образом:

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

Как видно, если идентификатор (IID) интерфейса распознан, возвращается указатель на объект; в противном случае возникает ошибка. Также обратите внимание, что результатом успешного QueryInterface является неявный AddRef. Конечно, необходимо также реализовать CEditObj::Print. Это просто, поскольку IPrintInterface непосредственно, является производным от интерфейса IUnknown. Однако при необходимости поддерживать два разных интерфейса, оба из которых являются производными от IUnknown, необходимо учитывать следующее:

class IEditInterface : public IUnkown
{
public:
    virtual void EditObject() = 0;
};

Хотя существует ряд различных способов реализовать класс, поддерживающий и IEditInterface, и IPrintInterface, включая использование множественного наследования C++, эта заметка будет сосредоточена на использовании вложенных классов для реализации этой функциональности.

class CEditPrintObj
{
public:
    CEditPrintObj();

    HRESULT QueryInterface(REFIID iid, void**);
    ULONG AddRef();
    ULONG Release();
    DWORD m_dwRef;

    class CPrintObj : public IPrintInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

    class CEditObj : public IEditInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
};

Ниже в разметке приведен полный пример.

CEditPrintObj::CEditPrintObj()
{
    m_editObj.m_pParent = this;
    m_printObj.m_pParent = this;
}

ULONG CEditPrintObj::AddRef() 
{ 
    return ++m_dwRef;
}

CEditPrintObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = &m_printObj;
        AddRef();
        return NOERROR;
    }
    else if (iid == IID_IEditInterface)
    {
        *ppvObj = &m_editObj;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

ULONG CEditPrintObj::CEditObj::AddRef() 
{ 
    return m_pParent->AddRef(); 
}

ULONG CEditPrintObj::CEditObj::Release() 
{ 
    return m_pParent->Release(); 
}

HRESULT CEditPrintObj::CEditObj::QueryInterface(
    REFIID iid, void** ppvObj) 
{ 
    return m_pParent->QueryInterface(iid, ppvObj); 
}

ULONG CEditPrintObj::CPrintObj::AddRef() 
{ 
    return m_pParent->AddRef(); 
}

ULONG CEditPrintObj::CPrintObj::Release() 
{ 
    return m_pParent->Release(); 
}

HRESULT CEditPrintObj::CPrintObj::QueryInterface(
    REFIID iid, void** ppvObj) 
{ 
    return m_pParent->QueryInterface(iid, ppvObj); 
}

Обратите внимание, что большинство реализации IUnknown помещается в класс CEditPrintObj вместо дублирования кода в CEditPrintObj::CEditObj и CEditPrintObj::CPrintObj. Это уменьшает объем кода и позволяет избежать ошибок. Ключевой момент здесь в том, что из интерфейса IUnknown можно вызвать QueryInterface для извлечения любого интерфейса, который может поддерживаться объектом, и из каждого из этих интерфейсов можно сделать то же самое. Это означает, что все функции QueryInterface, доступные из каждого интерфейса, должны вести себя одинаково. Чтобы эти встроенные объекты могли вызывать реализацию во "внешнем объекте", используется обратный указатель (m_pParent). M_pParent указатель инициализирован во время разработки CEditPrintObj. После этого можно будет реализовать CEditPrintObj::CPrintObj::PrintObject и CEditPrintObj::CEditObj::EditObject. Для добавления всего одной функции (возможность редактировать объект) было добавлено много кода. К счастью, интерфейсы достаточно редко имеют только одну функцию-член (хотя такое случается); в этом случае EditObject и PrintObject обычно объединяются в один интерфейс.

Для такого сценария существует множество пояснений и значительное количество кода. Классы MFC/OLE предоставляют более простой альтернативой. Реализация MFC использует метод аналогично тому, как сообщения Windows создаваться программу-оболочку со схемами сообщений. Это средство называется карты интерфейсов и см. в следующем разделе.

Сопоставления интерфейсов MFC

MFC/OLE включает реализацию "Схемы интерфейсов", схожие со схемами сообщений и схемами подготовки к отправке по концепции и исполнению. Основные функции сопоставлений интерфейсов MFC следующие:

  • Стандартная реализация IUnknown, встроенная в класс CCmdTarget.

  • Обслуживание счетчика ссылок, измененного AddRef и Release

  • Управляемая данными реализация QueryInterface

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

  • Поддержка создания COM-объектов с возможностью агрегации

  • Поддержка использования агрегатных объектов в реализации COM-объекта

  • Реализацию можно прикрепить и расширить

Дополнительные сведения об агрегировании см. в разделе Агрегирование.

Поддержка сопоставлений интерфейсов MFC реализована в классе CCmdTarget. CCmdTarget "имеет" счетчик ссылок, а также все функции-члены, связанные с реализацией IUnknown (счетчик ссылок, например, находится в CCmdTarget). Чтобы создать класс, поддерживающий OLE модели COM, необходимо наследовать класс от класса CCmdTarget и использовать различные макросы, а также функции-члены CCmdTarget для реализации требуемых интерфейсов. Реализация MFC использует вложенные классы для определения реализации каждого интерфейса, как в примере выше. Это можно сделать проще со стандартной реализацией IUnknown, точно так же как и несколько макросов, устраняющих определенный повторяющийся код.

Реализация класса с помощью сопоставлений интерфейсов MFC

  1. Класс должен наследоваться прямо или косвенно от CCmdTarget.

  2. Используйте функцию DECLARE_INTERFACE_MAP в определении производного класса.

  3. Для каждого интерфейса, который нужно поддерживать, необходимо использовать макросы BEGIN_INTERFACE_PART и END_INTERFACE_PART в определении класса.

  4. В файле реализации используйте макросы BEGIN_INTERFACE_MAP и END_INTERFACE_MAP для определения карты интерфейсов класса.

  5. Для каждого поддерживаемого IID используйте макрос INTERFACE_PART между макросами BEGIN_INTERFACE_MAP и END_INTERFACE_MAP для сопоставления этого IID с определенной "частью" класса.

  6. Реализуйте каждый из вложенных классов, представляющих поддерживаемые интерфейсы.

  7. Используйте макрос METHOD_PROLOGUE для доступа к родительскому объекту, производному от CCmdTarget.

  8. AddRef, Release и QueryInterface могут делегировать CCmdTarget-реализации этих функций (ExternalAddRef, ExternalRelease и ExternalQueryInterface).

В приведенном выше примере CPrintEditObj может быть реализован следующим образом:

class CPrintEditObj : public CCmdTarget
{
public:
    // member data and member functions for CPrintEditObj go here

// Interface Maps
protected:
    DECLARE_INTERFACE_MAP()

    BEGIN_INTERFACE_PART(EditObj, IEditInterface)
        STDMETHOD_(void, EditObject)();
    END_INTERFACE_PART(EditObj)

    BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
        STDMETHOD_(void, PrintObject)();
    END_INTERFACE_PART(PrintObj)
};

Создает объявление класса, производного от класса CCmdTarget. Макрос DECLARE_INTERFACE_MAP сообщает платформе, что этот класс имеет сопоставление пользовательского интерфейса. Кроме того, макросы BEGIN_INTERFACE_PART и END_INTERFACE_PART определяют вложенные классы, в данном случае с именами CEditObj и CPrintObj (X используется только для различения вложенных классов и глобальных классов, начинающихся с "C", и классов интерфейса, которые начинаются с "I"). Создаются два вложенных члена этих классов: m_CEditObj и m_CPrintObj, соответственно. Макросы автоматически объявляют AddRef, Выпуск и функцию QueryInterface; поэтому при объявлении только функции, относящиеся к этому интерфейсу. EditObject и PrintObject (OLE макрос STDMETHOD используется, чтобы _stdcall и виртуальные ключевые слова, предоставляемых при необходимости для платформы целевого объекта).

Реализация карты интерфейса для этого класса:

BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
    INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Это подключение IID_IPrintInterface IID с m_CPrintObj и IID_IEditInterface с m_CEditObj соответственно. Реализация CCmdTarget QueryInterface (CCmdTarget::ExternalQueryInterface) использует это сопоставление для возврата указатели на m_CPrintObj и m_CEditObj исключение. Не следует включать запись для IID_IUnknown; платформа будет использовать первый интерфейс в сопоставлении (в данном случае — IID_IUnknown. m_CPrintObj) при запросе.

Несмотря на то, что макрос BEGIN_INTERFACE_PART автоматически объявляет функции AddRef, Выпуск и QueryInterface, вам все равно необходимо их реализовать:

ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
    REFIID iid, void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    // code to "Edit" the object, whatever that means...
}

Реализация для CEditPrintObj::CPrintObj, была бы похожа на вышеуказанным определения для CEditPrintObj::CEditObj. Хотя можно было бы создать макрос, который использовался бы для автоматического создания этих функций (раньше в разработке MFC/OLE это делалось именно так), было бы трудно устанавливать точки останова, если макрос сформирует несколько строк кода. Поэтому этот код разворачивается вручную.

При использовании предусмотренной платформой реализации схем сообщений есть ряд действий, выполнять которые не требовалось:

  • Реализация QueryInterface

  • Реализация AddRef и Release

  • Объявление одного из этих встроенных методов для обоих интерфейсов

Кроме того, платформа использует схемы сообщений внутренне. Это означает, что наследование от класса платформы, допустим от COleServerDoc, который уже поддерживает несколько интерфейсов и предоставляет или замены или добавления в интерфейсы, предоставляемые средой выполнения. Это можно сделать, поскольку платформа полностью поддерживает наследование сопоставления интерфейсов от базового класса. То причина, по которой BEGIN_INTERFACE_MAP принимает в качестве второго параметра имя базового класса.

Примечание

Обычно невозможно повторно использовать реализацию встроенных реализаций MFC интерфейсов OLE путем простого наследования интегрированной специализации того интерфейса из версии MFC.Это невозможно, поскольку использование макроса METHOD_PROLOGUE для получения доступа к объекту, содержащему производный от CCmdTarget, подразумевает фиксированное смещение внедренного объекта из производного объекта CCmdTarget.Это означает, например, невозможно произвести внедренный XMyAdviseSink из реализации MFC в COleClientItem::XAdviseSink, поскольку XAdviseSink основывается на ячейки, в особенности — от верха объекта COleClientItem.

Примечание

Можно, однако, выполнить делегирование в реализацию MFC для всех функций, для которых требуется поведение MFC по умолчанию.Это можно сделать в реализации MFC IOleInPlaceFrame (XOleInPlaceFrame) в классе COleFrameHook (он делегирует в m_xOleInPlaceUIWindow для многих функций).Такая конструкция были выбраны для уменьшения размера среды выполнения являются объектами, которые реализуют множество интерфейсов; она позволяет обходиться без обратного указателя (в предыдущем разделе в качестве него, допустим, использовался m_pParent).

Агрегирование и карты интерфейсов

Помимо поддержки отдельных COM-объектов, MFC также поддерживает агрегирование. Агрегирование само по себе является слишком сложной темой, чтобы рассматривать ее здесь; дополнительные сведения об агрегировании см. в разделе Агрегирование. Это примечание просто описывает поддержку агрегата сборки в сопоставления платформы и интерфейса.

Существует два способа использования агрегации: с помощью COM-объекта, поддерживающего агрегацию, и (2) путем реализации объекта, который может агрегироваться другим. Эти возможности называются "использование агрегатного объекта" и "обеспечение поддержки агрегации объектов". MFC поддерживает оба.

Использование статистических объектов

Для использования агрегатного объекта требуется некоторый способ включения агрегата в механизм QueryInterface. Другими словами, агрегатный объект должен вести себя так, как будто он является собственной частью объекта. Итак, как это связано с механизмом сопоставления интерфейсов MFC? В дополнение к макросу INTERFACE_PART, где вложенный объект сопоставлен с IID, можно также объявить агрегатный объект как часть производного класса CCmdTarget. Для этого используется макрос INTERFACE_AGGREGATE. Это позволяет определить переменную-член (который должен быть указателем на IUnknown или производным классом), которая называется интегрированным в механизм сопоставления интерфейса. Если указатель не равен NULL, при вызове метода CCmdTarget::ExternalQueryInterface среда выполнения автоматически вызывает функцию-член QueryInterface агрегатного объекта, если запрошенный IID не является одним из собственных объектов IID, поддерживаемых самим объектом CCmdTarget.

Использование макроса INTERFACE_AGGREGATE

  1. Объявите переменную-член (IUnknown*), содержащую указатель на агрегатный объект.

  2. Включите в карту интерфейсов макрос INTERFACE_AGGREGATE, который ссылается на переменную-член по имени.

  3. В некоторый момент (обычно во время CCmdTarget::OnCreateAggregates) инициализируйте переменную-член каким-либо значением, отличным от NULL.

Примеры.

class CAggrExample : public CCmdTarget
{
public:
    CAggrExample();

protected:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

    DECLARE_INTERFACE_MAP()
    // "native" interface part macros may be used here
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL;
}

BOOL CAggrExample::OnCreateAggregates()
{
    // wire up aggregate with correct controlling unknown
    m_lpAggrInner = CoCreateInstance(CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID*)&m_lpAggrInner);
    if (m_lpAggrInner == NULL)
        return FALSE;
    // optionally, create other aggregate objects here
    return TRUE;
}

BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
    // native "INTERFACE_PART" entries go here
    INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

Переменная m_lpAggrInner инициализируется в конструкторе до NULL. Платформа игнорирует NULL переменную-член в реализации по умолчанию QueryInterface. OnCreateAggregates отлично подходит для начала создания агрегатных объектов. Необходимо явно вызвать его при создании объекта вне реализации MFC COleObjectFactory. Причина создания агрегатов в CCmdTarget::OnCreateAggregates и использования CCmdTarget::GetControllingUnknown становится очевидным при обсуждении создания агрегируемых объектов.

Таким образом объект получает все интерфейсы, поддерживаемые агрегатными объектом, а также свои собственные интерфейсы. Если требуется только подмножество интерфейсов, которые поддерживает агрегат, можно переопределить CCmdTarget::GetInterfaceHook. Это обеспечивает возможности подключения на очень низком уровне, аналогично QueryInterface. Обычно требуются все интерфейсы, которые поддерживает агрегат.

Включение поддержки агрегации реализации объектов

Для того, чтобы объект был агрегируемым, реализация AddRef, Release и QueryInterface должна делегировать "управляющему неизвестному". Другими словами, для того, чтобы он был частью объекта, он должен делегировать AddRef, Release и QueryInterface другому объекту, также производному от IUnknown. Это "управление неизвестным" предоставляется объекту в момент создания, то есть предоставляется реализации COleObjectFactory. Реализация этого связана с небольшим объемом служебных данных и в некоторых случаях может являться нежелательной, поэтому в MFC она не обязательна. Чтобы сделать объект агрегируемым, нужно вызвать метод CCmdTarget::EnableAggregation из конструктора объекта.

Если объект также использует агрегаты, необходимо обязательно передать правильное "управляющее неизвестное" агрегатным объектам. Обычно этот указатель IUnknown передается в объект при создании агрегата. Например, параметр pUnkOuter представляет собой "управляющее неизвестное" для объектов, созданных с помощью CoCreateInstance. Правильный указатель "контроля неизвестного" можно извлечь вызовом CCmdTarget::GetControllingUnknown. Значение, возвращенное из этой функции, однако недопустимо во время конструктора. По этой причине создавать агрегаты предлагается только в переопределении CCmdTarget::OnCreateAggregates, где возвращаемое значение из GetControllingUnknown является надежным, даже если оно создано из реализации COleObjectFactory.

Также обратите внимание, что объекты управляют нужным числом ссылок при добавлении или выпуске искусственных счетчиков ссылок. Для обеспечения этого всегда вызывайте ExternalAddRef и ExternalRelease, а не InternalRelease и InternalAddRef. Очень редко вызывается InternalRelease или InternalAddRef в классе, поддерживающем агрегацию.

Опорный материал

Расширенное использование OLE, такое как определение собственных интерфейсов или переопределение реализации интерфейсов OLE платформы, требует использования базового механизма карты интерфейсов.

В этом разделе описывается каждый макрос и интерфейсы API, которые используются для реализации этих дополнительных функций.

CCmdTarget::EnableAggregation — описание функции

void EnableAggregation();

Примечания

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

CCmdTarget::ExternalQueryInterface — описание функции

DWORD ExternalQueryInterface( 
   const void FAR* lpIID, 
   LPVOID FAR* ppvObj 
);

Примечания

Параметры

  • lpIID
    Далекий указатель на IID (первый аргумент QueryInterface)

  • ppvObj
    Указатель на IUnknown* (второй аргумент QueryInterface)

Примечания

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

CCmdTarget::ExternalAddRef — описание функции

DWORD ExternalAddRef();

Примечания

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

CCmdTarget::ExternalRelease — описание функции

DWORD ExternalRelease();

Примечания

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

DECLARE_INTERFACE_MAP — описание макроса

DECLARE_INTERFACE_MAP

Примечания

Используйте этот макрос в любом классе, производном от CCmdTarget, который будет иметь сопоставление интерфейса. Используется в целом так же, как и DECLARE_MESSAGE_MAP. Этот вызов макроса следует поместить в определение класса, обычно в заголовке (H-файл). Класс DECLARE_INTERFACE_MAP должен определить сопоставление с интерфейса в файле реализации (CPP) с помощью макросов BEGIN_INTERFACE_MAP и END_INTERFACE_MAP.

BEGIN_INTERFACE_PART и END_INTERFACE_PART — описания макросов

BEGIN_INTERFACE_PART( 
   localClass,
   iface 
);
END_INTERFACE_PART( 
   localClass 
)

Примечания

Параметры

  • localClass
    Имя класса, реализующего интерфейс .

  • iface
    Имя интерфейса, реализуемого с помощью данного класса

Примечания

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

Аргумент iface — это OLE интерфейс, который можно реализовать, например IAdviseSink или IPersistStorage (или собственный пользовательский интерфейс).

Аргумент localClass является именем локального класса, который будет определен. Перед именем будет автоматически добавлена буква "Х". Это соглашение об именовании используется, чтобы избежать конфликтов с глобальными классами с таким же именем. Кроме того, имя встроенного члена совпадает с именем localClass, за исключением префикса "m_x".

Примеры.

BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
   STDMETHOD_(void,OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
   STDMETHOD_(void,OnViewChange)(DWORD, LONG);
   STDMETHOD_(void,OnRename)(LPMONIKER);
   STDMETHOD_(void,OnSave)();
   STDMETHOD_(void,OnClose)();
END_INTERFACE_PART(MyAdviseSink)

определяет локальный класс с именем XMyAdviseSink, производный от IAdviseSink, и член класса, в котором он объявлен, называется m_xMyAdviseSink. Примечание.

Примечание

Строка начиная с STDMETHOD фактически копируются из OLE2.H и немного изменены.Копирование их из OLE2.H может уменьшить количество трудноразрешимых ошибок.

BEGIN_INTERFACE_MAP и END_INTERFACE_MAP — описания макросов

BEGIN_INTERFACE_MAP( 
   theClass,
   baseClass 
) 
END_INTERFACE_MAP

Примечания

Параметры

  • theClass
    Класс, в котором сопоставление интерфейса нужно указывать

  • baseClass
    Класс, от которого наследуется theClass.

Примечания

Макросы BEGIN_INTERFACE_MAP и END_INTERFACE_MAP используются в файле реализации для фактического определения карты интерфейсов. Для каждого реализованного интерфейса существует один или несколько вызовов макроса INTERFACE_PART. Для каждого агрегата, используемого классом, существует один вызов макроса INTERFACE_AGGREGATE.

INTERFACE_PART — описание макроса

INTERFACE_PART( 
   theClass,
   iid, 
   localClass 
)

Примечания

Параметры

  • theClass
    Имя класса, содержащего карту интерфейса.

  • iid
    Объект IID, для сопоставления с встроенным классом.

  • localClass
    Полное имя класса, включая пространство имен (менее "Х").

Примечания

Этот макрос используется между макросом BEGIN_INTERFACE_MAP и макросом END_INTERFACE_MAP для каждого интерфейса, поддерживаемого этим объектом. Он позволяет сопоставить IID члену класса, указанного параметрами theClass и localClass. "m_x" расположения добавляется в localClass автоматически. Обратите внимание, что несколько объектов IID может быть связано с одним членом. Это удобно при реализации только "наиболее производного" интерфейса и необходимости также предоставить все промежуточные интерфейсы. Хорошим примером этого является интерфейс IOleInPlaceFrameWindow. Иерархии выглядят следующим образом:

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Если объект реализует IOleInPlaceFrameWindow, клиент может запрашивать интерфейс (QueryInterface) для любого из следующих интерфейсов: IOleUIWindow, IOleWindow или IUnknown, помимо самого дальнего в цепочке наследования интерфейса IOleInPlaceFrameWindow (того, который вы собственно реализуете). Для обработки этого можно использовать несколько макросов INTERFACE_PART для сопоставления каждого базового интерфейса интерфейсу IOleInPlaceFrameWindow:

в файле определения класса:

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

в файле реализации класса:

BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
    INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

Платформа следит за IUnknown, поскольку требуются всегда.

INTERFACE_PART — описание макроса

INTERFACE_AGGREGATE( 
   theClass,
   theAggr 
)

Примечания

Параметры

  • theClass
    Имя класса, содержащего карту интерфейса.

  • theAggr
    Имя переменной члена, который будет агрегирован.

Примечания

Этот макрос используется, чтобы сообщить платформе, что класс использует статистический объект. Значение должно находиться в диапазоне от BEGIN_INTERFACE_PART до END_INTERFACE_PART. Агрегатный объект — это отдельный объект, производный от IUnknown. Используя агрегат и макрос INTERFACE_AGGREGATE, можно сделать так, чтобы все интерфейсы, поддерживаемые агрегатом, выглядели как непосредственно поддерживаемые объектом. Аргумент theAggr является именем переменной члена класса, который является производным от класса IUnknown (напрямую или косвенно). Все макросы INTERFACE_AGGREGATE должны следовать за макросом INTERFACE_PART, будучи помещенными на карту интерфейсов.

См. также

Другие ресурсы

Технические примечания по номеру

Технические примечания по категории