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

94

trace("Запросить интерфейс IZ");

IZ* pIZ = NULL;

hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ); if (SUCCEEDED(hr))

{

trace("Интерфейс IZ получен успешно"); pIZ->Fz();

pIZ->Release();

trace("Освободить интерфейс IZ");

}

else

{

trace("Не могу получить интерфейс IZ");

}

trace("Освободить интерфейс IX"); pIX->Release();

}

else

{

cout << "Клиент: \t\tНе могу создать компонент. hr = " << hex << hr << endl;

}

// Закрыть библиотеку COM

CoUninitialize();

return 0;

Листинг 7-1 Полный код клиента

Но CoCreateInstance недостаточно гибка

Создание объектов — очень важная операция в любой объектно-ориентированной системе. Прежде чем использовать объекты, их необходимо создать. Если каждый объект создается по-своему, то будет трудно использовать разные объекты полиморфно. Следовательно, желательно, чтобы создание объектов было как можно более гибким — а все компоненты, в свою очередь, создавались сходным образом. Чем гибче процесс создания, тем легче его адаптировать к нуждам множества компонентов.

Выше мы видели, что CoCreateInstance получает CLSID, создает соответствующий компонент и возвращает требуемый указатель на интерфейс. В большинстве случаев CoCreateInstance вполне достаточно. Однако CoCreateInstance недостаточно гибка, чтобы предоставить клиенту способ управления процессом создания компонента. Когда CoCreateInstance возвращает управление, компонент уже создан. Уже поздно управлять тем, где он загружается в память, или проверять, есть ли вообще у клиента право создавать компонент.

Проблема состоит в том, как управлять созданием компонента. Нам не нужно беспокоиться об управлении инициализацией компонента. Компонент нетрудно инициализировать через интерфейс, который можно запросить после создания. Но Вы не можете получить интерфейс компонента до тех пор, пока тот не создан, — а тогда уже поздно задавать условия создания.

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

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

На самом деле CoCreateInstance не создает компоненты непосредственно. Вместо этого она создает компонент, называемый фабрикой класса (class factory), который затем и порождает нужный компонент. Фабрика класса — это компонент, единственной задачей которого является создание других компонентов. Точнее, конкретная фабрика класса создает компоненты, соответствующие одному конкретному CLSID. Клиент использует поддерживаемые фабрикой класса интерфейсы для управления тем, как фабрика создает каждый компонент. Стандартный интерфейс создания компонентов — IClassFactory. Компоненты, создаваемые CoCreateInstance, порождаются именно при помощи этого интерфейса.

Теперь давайте посмотрим, как клиент может создать компонент, напрямую используя фабрику класса. Первый шаг — создание самой фабрики. Когда фабрика класса создана, мы используем некоторый интерфейс, подобный IClassFactory, для окончательного создания своего компонента.

95

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

CoCreateInstance получает CLSID и возвращает указатель на интерфейс компонента. Нам нужна эквивалентная функция, которая по CLSID возвращает указатель на интерфейс, принадлежащий фабрике класса данного CLSID. Такая функция в библиотеке СОМ существует и называется CoGetClassObject.

Объявление CoGetClassObject показано ниже:

HRESULT __stdcall CoGetClassObject( const CLSID& clsid,

DWORD dwClsContext,

COSERVERINFO* pServerInfo, // Зарезервировано для DCOM const IID& iid,

void** ppv

);

Как видите, CoGetClassObject очень похожа на CoCreateInstance. Первым параметром обеих функций является CLSID нужного компонента. Обеим также передается контекст выполнения — dwClsContext. Два последних параметра тоже совпадают. Но CoGetClassObject возвращает запрашиваемый указатель для фабрики класса, тогда как CoCreateInstance — для самого компонента. Отличаются эти функции только одним параметром.

CoCreateInstance принимает указатель IUnknown, тогда как CoGetClassObject — указатель на COSERVERINFO. COSERVERINFO используется DCOM для управления доступом к удаленным компонентам. Мы рассмотрим эту структуру в гл. 10.

Повторю, существенное различие между CoGetClassObject и CoCreateInstance состоит в том, что первая возвращает указатель, относящийся к фабрике класса нужного нам компонента, а не к самому компоненту. Требумый компонента создается при помощи интерфейса, указатель на который возвращает CoGetClassObject. Обычно это указатель IClassFactory.

IClassFactory

Стандартный интерфейс, который поддерживают фабрики класса для создания компонентов, — это IClassFactory. Объявление этого интерфейса приводится ниже:

interface IClassFactory : IUnknown

{

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

void** ppv); HRESULT __stdcall LockServer(BOOL bLock);

}

У IClassFactory есть две функции-члена, CreateInstance и LockServer. Обсуждение LockServer мы отложим до конца главы.

CreateInstance

IClassFactory::CreateInstance — это еще одна функция со знакомыми параметрами. Первый параметр, pUnknownOuter — указатель на интерфейс IUnknown. Это тот же самый указатель, что передается CoCreateInstance. Его мы рассмотрим в следующей главе при обсуждении агрегирования компонентов.

Два оставшихся параметра — те же, что у QueryInterface. С их помощью вызывающая функция может запросить интерфейс компонента одновременно с созданием последнего. Это экономит клиенту один вызов функции. Если компонент к тому же выполняется на удаленной машине, то так экономится и один цикл запрос-ответ по сети.

Самое интересное в CreateInstance — не те параметры, которые у нее есть, а параметр, которого у нее нет. IClassFactory::CreateInstance не получает в качестве параметра CLSID. Это означает, что данная функция может создавать компоненты, соответствующие только одному CLSID — тому, который был передан CoGetClassObject.

IClassFactory2

Microsoft уже объявила еще один интерфейс создания компонентов, дополняющий IClassFactory. IClassFactory2 добавляет к IClassFactory поддержку лицензирования или разрешения на создание. Клиент обязан передать фабрике класса при помощи IClassFactory2 корректный ключ или лицензию, прежде чем та будет создавать компоненты. С помощью IClassFactory2 фабрика класса может гарантировать, что клиент получил компонент легально и имеет право пользования. Я уверен, что это не последний интерфейс создания компонентов.

96

CoCreateInstance vs. CoGetClassObject

Создание фабрики класса, получение указателя IClassFactory и затем создание компонента — это большая работа, которую приходится проделывать всякий раз при создании компонента. Вот почему в большинстве программ используется CoCreateInstance, а не CoGetClassObject. Однако, как я уже говорил, CoCreateInstance в

действительности реализована при помощи CoGetClassObject. Ниже приведен код, показывающий, как это могло бы быть сделано.

HRESULT CoCreateInstance(const CLSID& clsid, IUnknown* pUnknownOuter, DWORD dwClsContext, const IID& iid,

void** ppv)

{

//Установить в NULL выходной параметр

*ppv = NULL;

//Создать фабрику класса и получить указатель на интерфейс IClassFactory IClassFactory* pIFactory = NULL;

HRESULT hr = CoGetClassObject(clsid,

dwClsContext,

NULL, IID_IClassFactory, (void**)&pIFactory);

if (SUCCEEDED(hr))

{

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

hr = pIFactory->CreateInstance(pUnknownOuter, iid, ppv);

// Освободить фабрику класса pIFactory->Release();

}

return hr;

}

CoCreateInstance вызывает CoGetClassObject и получает указатель на интерфейс IClassFactory фабрики класса. Затем с помощью полученного указателя CoCreateInstance вызывает IClassFactory::CreateInstance, результатом чего и является создание нового компонента.

Зачем нужна CoGetClassObject?

В большинстве случаев можно забыть о CoGetClassObject и создать компонент при помощи CoCreateInstance. Однако есть два случая, в которых следует использовать именно CoGetClassObject, а не CoCreateInstance. Вопервых, Вы должны использовать CoGetClassObject, если хотите создать объект с помощью интерфейса, отличного от IClassFactory. Так, если Вам нужен IClassFactory2, придется использовать CoGetClassObject. Вовторых, если Вы хотите сразу создать много компонентов, то эффективнее будет один раз создать фабрику класса для всех компонентов, вместо того, чтобы создавать и освобождать ее для каждого экземпляра компонента. CoGetClassObject дает клиенту столь необходимую возможность управления процессом создания.

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

Фабрики класса инкапсулируют создание компонентов

Мне бы хотелось отметить некоторые характеристики фабрик класса. Прежде чем показать Вам, как их реализовывать. Во-первых, данный экземпляр фабрики класса создает компоненты, соответствующие только одному CLSID. Это очевидно, поскольку CoGetClassObject имеет в качестве параметра CLSID, а IClassFactory::CreateInstance нет. Во-вторых, фабрика класса для данного CLSID создается тем же разработчиком, который реализует соответствующий компонент. Компонент-фабрика класса в большинстве случаев содержится в той же DLL, что и создаваемый компонент.

Конкретный экземпляр фабрики класса соответствует одному конкретному CLSID, и как фабрика, так и создаваемый ею компонент реализуются одним и тем же программистом. Поэтому фабрика класса может иметь и имеет специфические знания о создаваемом компоненте. Реализация фабрики класса с использованием специфической информации о создаваемом компоненте — отнюдь не программистская небрежность. Задача фабрики класса состоит в том, чтобы знать, как создается компонент, и инкапсулировать это знание так, чтобы максимально изолировать клиент от требований компонента.

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