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

97

Реализация фабрики класса

В этом разделе мы рассмотрим реализацию компонента, обращая особое внимание на реализацию фабрики класса. Но сначала посмотрим, как создаются сами фабрики класса.

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

В гл. 5 функция CallCreateInstance вызывала для создания компонента функцию CreateInstance из DLL. Функции CoGetClassObject также нужна точка входа DLL для создания фабрики класса компонента, которая (фабрика — ред.) реализована в одной DLL с компонентом. Эта точка входа называется DllGetClassObject. CoGetClassObject вызывает функцию DllGetClassObject, которая в действительности создает фабрику класса. DllGetClassObject объявлена так:

STDAPI DllGetClassObject( const CLSID& clsid, const IID& iid, void** ppv

);

Три параметра этой функции Вам уже знакомы: это те же параметры, что передаются CoGetClassObject. Первый

— идентификатор класса компонентов, которые будет создавать фабрика класса. Второй — идентификатор интерфейса фабрики, который желает использовать клиент. Указатель на этот интерфейс возвращается через третий параметр.

Весьма существенно, что DllGetClassObject передается CLSID. Этот параметр позволяет одной DLL поддерживать несколько компонентов, так как по значению CLSID можно выбрать подходящую фабрику класса.

Общая картина

Набросок общей картины создания компонента представлен на рис. 7-1. Здесь Вы увидите основных «участников» процесса. Во-первых, это клиент, который инициирует запрос обращением к CoGetClassObject. Вовторых, это библиотека СОМ, реализующая CoGetClassObject. В-третьих, это DLL. DLL содержит функцию

DllGetClassObject, которая вызывается CoGetClassObject. Задача DllGetClassObject — создать запрошенную фабрику класса. Способ, которым она это делает, оставлен полностью на усмотрение разработчика, так как он скрыт от клиента.

После того, как фабрика класса создана, клиент использует интерфейс IClassFactory для создания компонента. Как именно IClassFactory::CreateInstance создает компонент — дело разработчика. Как уже отмечалось, IClassFactory инкапсулирует этот процесс, поэтому при создании компонента фабрика класса может использовать специфические знания.

Клиент

 

Библиотека COM

 

DLL

3

 

 

 

Вызывает

1

CoGetClassObject

2

DllGetClassObject

Создает

 

 

фабрику

CoGetClassObject

 

 

 

 

 

 

класса

 

Возвращает

 

 

 

 

 

 

IClassFactory

 

4

 

 

 

 

5

Вызывает

 

 

 

 

pIClassFactory

IClassFactory::CreateInstance

 

IClassFactory

 

 

 

 

 

 

 

 

 

7

Возвращает IX

 

 

 

pIX

 

 

8

Вызывает IX::Fx

 

IX

6

 

 

 

 

 

 

 

 

 

 

Создает

 

 

 

 

 

 

 

 

 

 

 

 

 

 

компонент

Рис. 7-1 Пронумерованный точки показывают последовательность создания клиентом компонента с помощью библиотеки СОМ и фабрики класса

Теперь мы готовы рассмотреть реализацию компонента.

98

Листинг кода компонента

Реализация компонента и его фабрики класса показаны в листинге 7-2. Фабрика реализована классом С++ CFactory. Первое, что Вы должны заметить в CFactory — это просто еще один компонент. Он реализует IUnknown так же, как и другие компоненты. Единственное отличие между реализациями CFactory и CA составляют наборы поддерживаемых интерфейсов.

Просматривая код, особое внимание уделите CFactory::CreateInstance и DllGetClassObject.

CMPNT.CPP

//

// Cmpnt.cpp

//

#include <iostream.h> #include <objbase.h>

#include "Iface.h" // Объявления интерфейсов #include "Registry.h" // Функции для работы с Реестром

// Функция трассировки

void trace(const char* msg) { cout << msg << endl; }

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

//

// Глобальные переменные

//

// Описатель модуля DLL

static HMODULE g_hModule = NULL;

static long g_cComponents = 0;

// Количество активных компонентов

static long g_cServerLocks = 0;

// Счетчик блокировок

// Дружественное имя компонента

const char g_szFriendlyName[] = "Inside COM, Chapter 7 Example";

// Не зависящий от версии ProgID

const char g_szVerIndProgID[] = "InsideCOM.Chap07";

// ProgID

const char g_szProgID[] = "InsideCOM.Chap07.1";

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

//

// Компонент

//

class CA : public IX, public IY

{

public:

// IUnknown

virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef();

virtual ULONG __stdcall Release();

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

virtual void __stdcall Fx() { cout << "Fx" << endl; }

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

virtual void __stdcall Fy() { cout << "Fy" << endl; }

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

CA();

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

~CA();

private:

// Счетчик ссылок long m_cRef;

};

99

//

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

//

CA::CA() : m_cRef(1)

{

InterlockedIncrement(&g_cComponents);

}

//

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

//

CA::~CA()

{

InterlockedDecrement(&g_cComponents); trace("Компонент:\t\tСаморазрушение");

}

//

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

//

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)

{

if (iid == IID_IUnknown)

{

*ppv = static_cast<IX*>(this);

}

else if (iid == IID_IX)

{

*ppv = static_cast<IX*>(this); trace("Компонент:\t\tВернуть указатель на IX");

}

else if (iid == IID_IY)

{

*ppv = static_cast<IY*>(this); trace("Компонент:\t\tВернуть указатель на IY");

}

else

{

*ppv = NULL;

return E_NOINTERFACE;

}

reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK;

}

ULONG __stdcall CA::AddRef()

{

return InterlockedIncrement(&m_cRef);

}

ULONG __stdcall CA::Release()

{

if (InterlockedDecrement(&m_cRef) == 0)

{

delete this; return 0;

}

return m_cRef;

}

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

//

// Фабрика класса

//

class CFactory : public IClassFactory

{

public:

// IUnknown

virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);

100

virtual ULONG

__stdcall

AddRef();

virtual ULONG

__stdcall

Release();

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

 

virtual HRESULT __stdcall

CreateInstance(IUnknown* pUnknownOuter,

 

 

const IID& iid,

 

 

void** ppv);

virtual HRESULT __stdcall

LockServer(BOOL bLock);

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

 

 

CFactory() : m_cRef(1) {}

 

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

~CFactory() { trace("Фабрика класса:\t\tСаморазрушение"); }

private:

long m_cRef; };

//

// Реализация IUnknown для фабрики класса

//

HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)

{

if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))

{

*ppv = static_cast<IClassFactory*>(this);

}

else

{

*ppv = NULL;

return E_NOINTERFACE;

}

reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK;

}

ULONG __stdcall CFactory::AddRef()

{

return InterlockedIncrement(&m_cRef);

}

ULONG __stdcall CFactory::Release()

{

if (InterlockedDecrement(&m_cRef) == 0)

{

delete this; return 0;

}

return m_cRef;

}

//

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

//

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

void** ppv)

{

trace("Фабрика класса:\t\tСоздать компонент");

//Агрегирование не поддерживается if (pUnknownOuter != NULL)

{

return CLASS_E_NOAGGREGATION;

}

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

CA* pA = new CA; if (pA == NULL)

101

{

return E_OUTOFMEMORY;

}

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

HRESULT hr = pA->QueryInterface(iid, ppv);

//Освободить указатель на IUnknown

//(При ошибке в QueryInterface компонент разрушит сам себя)

pA->Release(); return hr;

}

// LockServer

HRESULT __stdcall CFactory::LockServer(BOOL bLock)

{

if (bLock)

{

InterlockedIncrement(&g_cServerLocks);

}

else

{

InterlockedDecrement(&g_cServerLocks);

}

return S_OK;

}

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

//

// Экспортируемые функции

//

//

// Можно ли выгружать DLL?

//

STDAPI DllCanUnloadNow()

{

if ((g_cComponents == 0) && (g_cServerLocks == 0))

{

return S_OK;

}

else

{

return S_FALSE;

}

}

//

// Получить фабрику класса

//

STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv)

{

trace("DllGetClassObject:\tСоздать фабрику класса");

//Можно ли создать такой компонент? if (clsid != CLSID_Component1)

{

return CLASS_E_CLASSNOTAVAILABLE;

}

//Создать фабрику класса

CFactory* pFactory = new CFactory;

// Счетчик ссылок устанавливается

if (pFactory == NULL)

// в конструкторе в 1

 

{

 

return E_OUTOFMEMORY;

 

}

 

102

// Получить требуемый интерфейс

HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release();

return hr;

}

//

// Регистрация сервера

//

STDAPI DllRegisterServer()

{

return RegisterServer(g_hModule, CLSID_Component1, g_szFriendlyName, g_szVerIndProgID, g_szProgID);

}

//

// Удаление сервера из Реестра

//

STDAPI DllUnregisterServer()

{

return UnregisterServer(CLSID_Component1, g_szVerIndProgID, g_szProgID);

}

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

//

// Реализация модуля DLL

//

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)

{

if (dwReason == DLL_PROCESS_ATTACH)

{

g_hModule = hModule;

}

return TRUE;

}

Листинг 7-2 Полный код компонента, фабрики класса и функций, экспортируемых из DLL

Клиент:

Вызвать CoCreateInstance для

Клиент:

создания компонента и получения интерфейса IX

DllGetClassObject:

Создать фабрику класса

Фабрика класса:

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

Компонент:

Вернуть указатель на IX

Фабрика класса:

Саморазрушение

Клиент:

IX получен успешно

Fx

Запросить интерфейс IX

Клиент:

Компонент:

Вернуть указатель на IY

Клиент:

IY получен успешно

Fy

Освободить интерфейс IY

Клиент:

Клиент:

Запросить интерфейс IZ

Клиент:

Не могу получить интерфейс IZ

Клиент:

Освободить интерфейс IX

Компонент:

Саморазрушение

Только что представленная реализация DllGetClassObject делает три вещи. Во-первых, она проверяет, соответствует ли запрос именно той фабрике, которую она умеет создавать. Затем при помощи операции new создается фабрика класса. Наконец, DllGetClassObject запрашивает у фабрики класса интерфейс, требуемый клиенту. Реализация IClassFactory::CreateInstance похожа на реализацию DllGetClassObject. Обе функции создают компонент и запрашивают у него интерфейс. IClassFactory::CreateInstance создает CA, тогда как

DllGetClassObject CFactory.

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