§ 11. Динамическая компоновка.
§ 11.1. Хранение
компонентов.
После реализации компонента на том или
ином языке программирования (в нашем случае С++) он должен выполняться как
процесс в определенной операционной системе. СОМ-компоненты охраняться либо в
исполняемых файлах (EXE), либо в файлах динамически
загруженных библиотек Windows (DLL), либо в тех и других. Каждый вариант хранения имеет
свои преимущества, и разработчик компонентов должен реализовывать определенные
функции различным образом в зависимости от выбранного им способа размещения
компонентов.
1) термин локальный сервер используется для описания компонентов,
которые храниться в исполняемых файлах. Такие файлы, помимо СОМ-компонентов,
могут содержать и другие функции.
Например, Microsoft Word является локальным сервером. Он не только
обеспечивает возможности обработки текстов, но также представляет множество
СОМ-компонентов, к которым имеют доступ другие приложения.
2) Внутризадачный сервер представляет
собой DLL-файл Windows.
Выполнение этого компонента происходит в контексте вызывающего процесса и
процесс клиента имеет прямой доступ к любому DLL-компоненту.
Понятие внутризадачности сервера приобретает особую значимость при
рассмотрении пользовательских СОМ-интерфейсов и процессов транспортировки.
3) Удаленный сервер представляет собой компонент, загружаемый и
выполняемый на удаленном компьютере. Обычно удаленный сервер строится как
исполняемый файл, однако это не является обязательным требованием. Доступ к
компонентам. Хранящимся только в DLL-файлах,
может осуществляться и в удаленном режиме. В таких случаях модель СОМ создает суррогатный
процесс, в котором могут выполняться удаленные DLL-файлы.
Одним из преимуществ СОМ является
независимость клиента (пользователя компонента) от местонахождения компонента,
исходя СОМ-серверы могут быть реализованы в трех различных конфигурациях,
клиенту при этом не требуется знать, каким образом реализован или где находятся
необходимые серверы. Он создает экземпляр компонента, а расположение и запуск
возлагаются на средства СОМ. Таким образом, многосвязность является
неотъемлемыми свойством СОМ-компонентов.
Наиболее эффективными являются внутризадачные
серверы поскольку они выполняются в адресном пространстве клиента, и, кроме
того, не требуется никаких дополнительных средств для передачи параметров
методам компонента.
§ 11.2. Создание компонента.
Для динамической компоновки необходимы
отдельный файлы для клиента и компонента. Клиент создает компонент,
содержащийся в DLL, то есть компонент динамически
компонуется с клиентом. Прежде чем запросить указатель на интерфейс, клиент
должен загрузить DLL в свой процесс и создать
компонент. В предыдущей задаче функция CreateInstance создавала компонент и возвращала указатель на интерфейс IUnknown. CreateInstance –
единственная функция в DLL, с которой
клиент должен быть скомпонован явно. [Ко всем прочим функциям компонента клиент
может получить доступ через указатель на интерфейс]. Чтобы клиент мог вызывать
функцию CreateInstance, ее надо экспортировать.
§ 11.2.1. Экспорт функции из DLL.
Для экспорта функции, CreateInstance, которая находится в файле компонента CMPNT1.CPP, необходимо:
1) отметить ее как extern "C”, то есть
обеспечить использование компоновки;
2) сообщить компоновщику, что функция
экспортируется . Для этого надо создать файл DEF.
Пример DEF-файла для CMPNT1.DLL:
[его
можно скопировать из примера и изменить несколько строк]
;
; файл определения модуля для Cmpnt1
;
LIBRARY Cmpnt1.dll
DISCRIPTION ‘2004 г. III курс’
EXPORTS
CreateInstance @1 PRIVATE
§ 11.2.2. Загрузка DLL.
Файлы клиента CREATE.H и CREATE.CPP реализуют функцию [CALLCreateInstance], которая принимает имя DLL и вызывает экспортированную функцию с именем CreateInstance. Используя функции Win32 LoadLibrary и GetProcAddres клиент может динамически компоноваться с компонентом
// Create.cpp
#include <iostream.h>
#include <unknwn.h> // объявляет
IUnknown
#include <Create.h>
typedef IUnknown* (*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”);
if (СreateInstance == NULL)
{
cout <<”CallСreateInstance:
\t Ошибка:”
<<”Не могу наитии функцию СreateInstance”
<< endl;
return Null;
}
return СreateInstance ( );
}
//Для загрузки DLL CallСreateInstance вызывает функцию Win32 LoadLibrary:
HIHNSTANCE LoadLibrary (LPCTSTR lpLIbFIleName //имя файла DLL);
LoadLibrary принимает
в качестве параметра имя файла DLL и возвращает описатель загруженной DLL. Функция Win32 GetProcAddress принимает этот описатель и имя функции
(СreateInstance), возвращая адрес последней.
FAR Proc Get ProcAddress (
HMODULE hModule, //описатель модуля DLL
LPCSTR
lpProcName //имя функции);
С помощью двух этих функций клиент может загрузить DLL в свое адресное пространство и получить адрес СreateInstance. Имея этот адрес, создать компонент и получить на его
IUnknown.

Рис.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.
Там же содержаться объявления для идентификаторов этих интерфейсов (ecxtern const IDD IDD _ X …).
Определения данных идентификаторов находятся в файле CUIDS.CPP.
//Create.h
IUnknown*callСreateInstance (char*name)
Для DLL шаблон
прописать все используемые функции
Собрать клиент и компонент с помощью
следующих команд:
cl Client.cpp Create.cpp
Guids.cpp UUID.LIB
cl /LD (LD-для динамической компоновки) Cmpnt1.cpp GUIDS.cpp UUID.LIB cmpnt1.def