Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Роджерсон Д. - Основы COM - 2000.pdf
Скачиваний:
412
Добавлен:
13.08.2013
Размер:
2.4 Mб
Скачать

152

if (pData->IsClassID(clsid))

{

//Идентификатор класса найден в массиве компонентов,

//которые мы можем создать. Поэтому создадим фабрику

//класса для данного компонента. Чтобы задать фабрике

//класса тип компонентов, которые она должна создавать,

//ей передается структура CFactoryData

*ppv = (IUnknown*) new CFactory(pData); if (*ppv == NULL)

{

return E_OUTOFMEMORY;

}

return NOERROR;

}

}

return CLASS_E_CLASSNOTAVAILABLE;

}

Листинг 9-5 Реализация CFactory::GetClassObject

Реализация CreateInstance в CFACTORY.CPP

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid,

void** ppv)

{

// При агрегировании IID должен быть IID_IUnknown

if ((pUnknownOuter != NULL) && (iid != IID_IUnknown))

{

return CLASS_E_NOAGGREGATION;

}

//Создать компонент

CUnknown* pNewComponent;

HRESULT hr = m_pFactoryData->CreateInstance(pUnknownOuter, &pNewComponent); if (FAILED(hr))

{

return hr;

}

//Initialize the component.

hr = pNewComponent->Init(); if (FAILED(hr))

{

// Ошибка инициализации. Удалить компонент pNewComponent->NondelegatingRelease(); return hr;

}

// Получить запрошенный интерфейс

hr = pNewComponent->NondelegatingQueryInterface(iid, ppv);

// Освободить ссылку, удерживаемую фабрикой класса pNewComponent->NondelegatingRelease();

return hr;

}

Листинг 9-6 Реализация CFactory::CreateInstance

Вот и все тонкости создания компонентов с помощью CFactory. Реализуйте компонент и поместите его данные в структуру — это все!

Использование CUnknown и CFactory

Я очень рад, что теперь мы сможем повторно использовать реализацию интерфейс IUnknown и фабрики класса. Вам наверняка уже надоел один и тот же код QueryInterface, AddRef и Release. Я тоже устал от него. Отныне наши компоненты не будут реализовывать AddRef и Release, а будут лишь добавлять поддержку нужных интерфейсов в QueryInterface. Мы также сможем использовать простую функцию создания, а не писать заново целую фабрику класса. Наши новые клиенты будут похожи на клиент, представленный в листинге 9-7.

153

CMPNT2.H

//

// Cmpnt2.h - Компонент 2

//

#include "Iface.h"

#include "CUnknown.h"// Базовый класс для IUnknown

///////////////////////////////////////////////////////////

//

// Компонент B

//

class CB : public CUnknown, public IY

{

public:

// Создание

static HRESULT CreateInstance(IUnknown* pUnknownOuter, CUnknown** ppNewComponent);

private:

//Объявление делегирующего IUnknown DECLARE_IUNKNOWN

//Неделегирующий IUnknown

virtual HRESULT __stdcall

NondelegatingQueryInterface(const IID& iid, void** ppv);

// Интерфейс IY

virtual void __stdcall Fy();

//Инициализация virtual HRESULT Init();

//Очистка

virtual void FinalRelease();

// Конструктор

CB(IUnknown* pUnknownOuter);

//Деструктор

~CB();

//Указатель на внутренний агрегируемый объект

IUnknown* m_pUnknownInner;

//Указатель на интерфейс IZ, поддерживаемый внутренним компонентом

IZ* m_pIZ;

};

Листинг 9-7 Компонент, использующий IUnknown, реализованный в CUnknown

В листинге 9-7 представлен заголовочный файл для Компонента 2 из примера этой главы. Код мы рассмотрим чуть позже. В этом примере Компонент 1 реализует интерфейс IX самостоятельно. Для того, чтобы предоставить интерфейсы IY и IZ, он агрегирует Компонент 2. Компонент 2 реализует IY и агрегирует Компонент 3, который, в свою очередь, реализует IZ. Таким образом, Компонент 2 — одновременно и агрегируемый, и агрегирующий.

Посмотрим листинг 9-7 от начала до конца. Я отмечу все интересные моменты, а затем мы рассмотрим их подробно.

Компонент наследует CUnknown, который предоставляет реализацию IUnknown. Мы объявляем статическую функцию, которую CFactory будет использовать для создания компонента. Имя этой функции для CFactory не имеет значения, поэтому можно назвать ее как угодно.

Далее мы реализуем делегирующий IUnknown с помощью макроса DECLARE_IUNKNOWN. DECLARE_IUNKNOWN реализует делегирующий IUnknown, а CUnknown — неделегирующий.

Хотя CUnknown полностью реализует AddRef и Release, он не может предоставить полной реализации QueryInterface, так как ему неизвестно, какие интерфейсы поддерживает наш компонент. Поэтому компонент реализует NondelegatingQueryInterface для обработки запросов на его собственные интерфейсы.

154

Производные классы переопределяют Init для создания внутренних компонентов при агрегировании или включении. CUnknown::NondelegatingRelease вызывает FinalRelease непосредственно перед тем, как удалить объект. Последнюю переопределяют те компоненты, которым необходимо освободить указатели на внутренние компоненты. CUnknown::FinalRelease увеличивает счетчик ссылок, чтобы предотвратить рекурсивную ликвидацию компонента.

Теперь рассмотрим различные аспекты Компонента 2, код которого представлен в листинге 9-8.

CMPNT2.CPP

//

// Cmpnt2.cpp - Компонент 2

//

#include <objbase.h>

#include "Iface.h" #include "Util.h"

#include "CUnknown.h"// Базовый класс для IUnknown #include "Cmpnt2.h"

static inline void trace(char* msg)

{ Util::Trace("Компонент 2", msg, S_OK); }

static inline void trace(char* msg, HRESULT hr) { Util::Trace("Компонент 2", msg, hr); }

///////////////////////////////////////////////////////////

//

// Реализация интерфейса IY

//

void __stdcall CB::Fy()

{

trace("Fy");

}

//

// Конструктор

//

CB::CB(IUnknown* pUnknownOuter)

: CUnknown(pUnknownOuter), m_pUnknownInner(NULL), m_pIZ(NULL)

{

// Пустой

}

//

// Деструктор

//

CB::~CB()

{

trace("Самоликвидация");

}

//

// Реализация NondelegatingQueryInterface

//

HRESULT __stdcall CB::NondelegatingQueryInterface(const IID& iid, void** ppv)

{

if (iid == IID_IY)

{

return FinishQI(static_cast<IY*>(this), ppv);

}

else if (iid == IID_IZ)

{

return m_pUnknownInner->QueryInterface(iid, ppv);

}

else

{

return CUnknown::NondelegatingQueryInterface(iid, ppv);

}

}

155

//

// Инициализировать компонент и создать внутренний компонент

//

HRESULT CB::Init()

{

trace("Создание агрегируемого Компонента 3");

HRESULT hr = CoCreateInstance(CLSID_Component3, GetOuterUnknown(),

CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pUnknownInner);

if (FAILED(hr))

{

trace("Не могу создать внутренний компонент", hr); return E_FAIL;

}

trace("Получить указатель на интерфейс IZ для использования в дальнейшем"); hr = m_pUnknownInner->QueryInterface(IID_IZ, (void**)&m_pIZ);

if (FAILED(hr))

{

trace("Внутренний компонент не поддерживает IZ", hr); m_pUnknownInner->Release();

m_pUnknownInner = NULL; return E_FAIL;

}

// Компенсировать увеличение счетчика ссылок из-за вызова QI trace("Указатель на интерфейс IZ получен. Освободить ссылку.");

GetOuterUnknown()->Release();

return S_OK;

}

//

// FinalRelease – Вызывается из Release перед удаление компонента

//

void CB::FinalRelease()

{

//Вызов базового класса для увеличения m_cRef и предотвращения рекурсии

CUnknown::FinalRelease();

//Учесть GetOuterUnknown()->Release в методе Init

GetOuterUnknown()->AddRef();

//Корректно освободить указатель, так как подсчет ссылок

//может вестись поинтерфейсно

m_pIZ->Release();

//Освободить внутренний компонент

//(Теперь мы можем это сделать, так как освободили его интерфейсы)

if (m_pUnknownInner != NULL)

{

m_pUnknownInner->Release();

}

}

///////////////////////////////////////////////////////////

//

// Функция создания для CFactory

//

HRESULT CB::CreateInstance(IUnknown* pUnknownOuter, CUnknown** ppNewComponent)

{

*ppNewComponent = new CB(pUnknownOuter); return S_OK;

}

Листинг 9-8 Реализация компонента, использующего CUnknown и CFactory

156

NondelegatingQueryInterface

Вероятно, самая интересная часть компонента — NondelegatingQueryInterface. Мы реализуем ее почти так же, как QueryInterface в предыдущих главах. Обратите, однако, внимание на два отличия. Во-первых, мы используем функцию FinishQI, причем делаем это лишь для удобства; мы не обязаны ее использовать. FinishQI лишь несколько облегчает реализацию NondeletgatingQueryInterface в производных классах. Код этой функции показан ниже:

HRESULT CUnknown::FinishQI(IUnknown* pI, void** ppv)

{

*ppv = pI; pI->AddRef(); return S_OK;

}

Второе отличие в том, что нам нет необходимости обрабатывать запрос на IUnknown. Базовый класс выполняет обработку для IUnknown и всех интерфейсов, о которых мы не знаем:

HRESULT __stdcall CUnknown::NondelegatingQueryInterface(const IID& iid, void** ppv)

{

// CUnknown поддерживает только IUnknown if (iid == IID_IUnknown)

{

return FinishQI(reinterpret_cast<IUnknown*> (static_cast<INondelegatingUnknown*>(this)), ppv);

}

else

{

*ppv = NULL;

return E_NOINTERFACE;

}

}

Все вместе, шаг за шагом

Приведенный выше код показывает, как легко писать компоненты с помощью CUnknown и CFactory. Давайте рассмотрим всю процедуру целиком. Далее приводится последовательность шагов создания компонента, его фабрики класса и DLL, в которой он будет находиться:

1. Напишите класс, реализующий компонент.

!" Базовым классом компонента должен быть либо CUnknown, либо другой класс, производный от него. !" При помощи макроса DECLARE_IUNKNOWN реализуйте делегирующий IUnknown.

!" Инициализируйте CUnknown в конструкторе своего класса.

!" Реализуйте NondelegatingQueryInterface, добавив интерфейсы, которые поддерживает Ваш класс, но не поддерживает базовый класс. Вызовите базовый класс для тех интерфейсов, которые не обрабатываются в Вашем классе.

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

!" Если после освобождения, но перед удалением компонент должен выполнить какую-либо очистку, реализуйте FinalRelease. Освободите указатели на включаемые и агрегируемые компоненты.

!" Реализуйте для своего компонента статическую функцию CreateInstance. !" Реализуйте поддерживаемые компонентом интерфейсы.

2.Повторите шаг 1 для каждого из компонентов, которые Вы хотите поместить в данную DLL.

3.Напишите фабрику класса.

!" Создайте файл для определения глобального массива CfactoryData g_FactoryDataArray.

!" Определите g_FactoryDataArray и поместите в него информацию о всех компонентах, обслуживаемых этой DLL.

!" Определите g_cFactoryDataEntries, которая должна содержать число компонентов в массиве g_FactoryDataArray.

4.Создайте DEF файл с описанием точек входа в DLL.

Соседние файлы в предмете Программирование на C++