После реализации компонента на том или
ином языке программирования (в нашем случае С++) он должен выполняться как
процесс в определенной операционной системе. СОМ-компоненты охраняться либо в
исполняемых файлах (EXE), либо в файлах динамически
загруженных библиотек Windows (DLL), либо в тех и других. Каждый вариант хранения имеет
свои преимущества, и разработчик компонентов должен реализовывать определенные
функции различным образом в зависимости от выбранного им способа размещения
компонентов.
1) термин локальный сервериспользуется для описания компонентов,
которые храниться в исполняемых файлах. Такие файлы, помимо СОМ-компонентов,
могут содержатьи другие функции.
Например, MicrosoftWord является локальным сервером. Он не только
обеспечивает возможности обработки текстов, но также представляет множество
СОМ-компонентов, к которым имеют доступ другие приложения.
2) Внутризадачный сервер представляет
собой DLL-файл Windows.
Выполнение этого компонента происходит в контексте вызывающего процесса и
процесс клиента имеет прямой доступ к любому DLL-компоненту.Понятие внутризадачности сервера приобретает особую значимость при
рассмотрении пользовательских СОМ-интерфейсов и процессов транспортировки.
3) Удаленный серверпредставляет собой компонент, загружаемый и
выполняемый на удаленном компьютере. Обычно удаленный сервер строится как
исполняемый файл, однако это не является обязательным требованием. Доступ к
компонентам. Хранящимся только в DLL-файлах,
может осуществляться и в удаленном режиме. В таких случаях модель СОМ создает суррогатный
процесс, в котором могут выполняться удаленные DLL-файлы.
Одним из преимуществ СОМ является
независимость клиента (пользователя компонента) от местонахождения компонента,
исходя СОМ-серверы могут быть реализованы в трех различных конфигурациях,
клиенту при этом не требуется знать, каким образом реализован или где находятся
необходимые серверы. Он создает экземпляр компонента, а расположение и запуск
возлагаются на средства СОМ. Таким образом, многосвязность является
неотъемлемыми свойством СОМ-компонентов.
Наиболее эффективными являются внутризадачные
серверы поскольку они выполняются в адресном пространстве клиента, и, кроме
того, не требуется никаких дополнительных средств для передачи параметров
методам компонента.
Для динамической компоновки необходимы
отдельный файлы для клиента и компонента. Клиент создает компонент,
содержащийся в DLL, то есть компонент динамически
компонуется с клиентом. Прежде чем запросить указатель на интерфейс, клиент
должен загрузить DLL в свой процесс и создать
компонент. В предыдущей задаче функция CreateInstance создавала компонент и возвращалауказатель на интерфейс IUnknown. CreateInstance –
единственная функция в DLL, с которой
клиент должен быть скомпонован явно. [Ко всем прочим функциям компонента клиент
может получить доступ через указатель на интерфейс]. Чтобы клиент мог вызывать
функцию CreateInstance, ее надо экспортировать.
ФайлыклиентаCREATE.H иCREATE.CPP реализуютфункцию[CALLCreateInstance], которая принимает имя DLL и вызывает экспортированную функцию с именем CreateInstance. Используя функции Win32 LoadLibrary и GetProcAddres клиент может динамически компоноваться с компонентом
// Create.cpp
#include <iostream.h>
#include <unknwn.h> // объявляет
IUnknown
#include <Create.h>
typedefIUnknown* (*CREATEFUNCPTR);//функция
//возвращает указатель //приведенного типа
//определим
функцию CallСreateInstance для создания компонента //приводит
возвращенный указатель к типу, пригодному для использования
IUncnown*
CallСreateInstance (char*name)
{
//загрузить
в процесс динамическую библиотеку
HIHNSTANCE hComponent = : : LoadLibrary (name);
if (nComponent == Null)
{
cout <<”CallСreateInstance: \t
Ошибка: немогузагрузитькомпонент.” <<endl;
return NULL;
}
//получитьадресфункцииСreateInstance
CREATEFUNCPTR СreateInstance
=( CREATEFUNCPTR) : : Get ProcAddress (hComponent, "СreateInstance”);
LoadLibraryпринимает
в качестве параметра имя файла DLL и возвращает описатель загруженной DLL. Функция Win32 GetProcAddress принимает этот описатель и имя функции
(СreateInstance), возвращая адрес последней.
FAR Proc Get ProcAddress (
HMODULE hModule, //описательмодуля DLL
LPCSTRlpProcName//имя функции);
С помощью двух этих функций клиент может загрузить DLL в свое адресное пространство и получить адрес СreateInstance. Имея этот адрес, создать компонент и получить на его
IUnknown.
DLL можно использовать для реализации компонентов,
потому что DLL использует адресное
пространство приложения, с которым
скомпонованы. В Windowsисполняющаяся программа называется процессом.
Каждое приложение (EXE) исполняется в отдельном процессе, и у каждогопроцесса имеется свое адресное пространство в
4Гбайт. Адрес в одном процессе отличен от того же адреса в другом процессе.
Указатели не могут чередоваться из одного приложения в другое, поскольку они
находятся в различных адресных пространствах. Указатели в двух процессах могут
иметь одно и то же значение, но фактически они будут указывать на разные
участки памяти.
Рис.11.1. Динамически компонуемые библиотеки размещаются в адресном
пространстве процесса, содержащего приложения, с которым они скомпонованы.
Поскольку DLL и EXE используют
один и тот же процесс, они используют одно и то же адресное пространство. По
этой причине DLL часто называют серверами
внутри процесса. [серверы вне процесса]
Итак, нам необходимо разбить программу
на несколько файлов.
Рис. 11.2. Файлы клиента1 и компонента1.
Теперь клиент находится в файле CLIENT1.CPP. Он включает
в себя файл Create.cpp (реализация CallСreateInstance и получение адреса СreateInstance). Эти два файла инкапсулируют создание компонента,
находящегося в DLL. [В дальнейшем эти файлы
заменяют функции, предоставляемые библиотекой COM]. Компонент теперь размещается в файле CMDNT1.CPP. Для
динамической компоновки требуется файл определения модуля, в котором
перечисляются функции, экспортируемые из DLL. Это файл CMPNT1.DEF. Компонент и клиент используют два общих файла. Файл IFACE.H содержит объявления
для всех интерфейсов, поддерживаемых CMPNT1.
Там же содержаться объявления для идентификаторов этих интерфейсов (ecxternconstIDDIDD _ X …).
Определения данных идентификаторов находятся в файле CUIDS.CPP.
//Create.h
IUnknown*callСreateInstance (char*name)
Для DLL шаблон
прописать все используемые функции
Собрать клиент и компонент с помощью
следующих команд: