Часть 1. Разработка простого внутрипроцессного сервера СОМ, реализованного в виде библиотеки DLL

Методические указания.Последовательность создания простого COM сервера в MVS-2008 с использованием ATL (Active Template Library – библиотека активных шаблонов) состоит из следующих основных этапов:

· создание каркаса приложения-библиотеки с помощью мастера ATL Project;

· создание СОМ объекта с фабрикой классов с помощью мастера ATL Simple Object;

· добавление в СОМ объект интерфейсов, объявлений методов и их реализаций;

· построение и регистрация (в реестре Windows) СОМ объекта, реализованного в виде внутрипроцессного сервера, т.е. DLL-библиотеки.

Шаг 1.Создание каркаса внутрипроцессного сервера.

Так как в этой работе нам надо будет создать два приложения – внутрипроцессный сервер и клиентское приложение – рекомендую создать для них общую папку с именем, например, Lab5 и в этой папке создавать проекты, каждый из которых, в свою очередь, будет размещаться в отдельной папке.

Выполнить тему меню FileèNewèProject. Как и при разработке любого другого приложения, в появившемся окне необходимо задать путь к проекту в поле ввода Location (…\Lab5) и имя проекта DLLMathServer в поле ввода Name. Выберите Project types=ATL и Templates=ATL Project, нажмите ОК.

На следующей странице Application Settings выберите только переключатель Server type=Dynamic-link library и не выбирайте никаких опций в группе Additional options, хотя это и не существенно для данного приложения. Впрочем, это только рекомендация: вы можете делать все что угодно, если любите преодолевать трудности. Нажмите кнопарек Finish – хотя это еще отнюдь не финиш, а пока просто завершение создания каркаса приложения.

Вам разрешается, совершенно безвозмездно, т.е. даром, посмотреть на файлы, сгенерированные мастером: DLLMathServer.cpp и DLLMathServer.def. Во втором файле (DLLMathServer.def) вы обнаружите, что библиотека экспортирует – по собственной инициативе – четыре важные функции:

· DllCanUnloadNow();

· DllGetClassObject();

· DllRegisterServer() и

· DllUnregisterServer().

Назначение этих функций подсказывают их имена. Интерес представляет также файл DLLMathServer.idl, к содержимому которого мы еще будем обращаться. В этом файле будет содержаться описание СОМ объекта и его методов на языке описания интерфейсов (IDL – interface definition language).

Шаг 2. Включение в библиотеку COM-объекта.

Выполните команду ProjectèAdd class. В появившемся окне надо выбрать Categories==ATL(не выбирайте WMI) и Templates==ATL Simple Object и нажать кнопку Add. В следующем окне на вкладке Names достаточно ввести только короткое имя объекта в поле Short name, а остальные имена мастер сгенерирует сам и их можно не изменять. В данном примере для объекта выбрано имя Mathem(рис. 1).



Часть 1. Разработка простого внутрипроцессного сервера СОМ, реализованного в виде библиотеки DLL - student2.ru

Рис.1. Задание свойств СОМ объекта

Естественно, вы обратили внимание на то, что мастер все остальные имена сформировал по короткому (Mathem) имени СОМ объекта. В частности, обратите внимание на поле ProgID: это так называемое внешнее имя СОМ сервера, под которым он будет зарегистрирован в реестре и которое можно будет использовать в клиентских приложениях для идентификации СОМ сервера. Естественно, что вы можете изменить имена, сформированные мастером, но … стоит ли это делать?

Нажмите кнопку Next или, что-то же самое, перейдите к вкладке Options. На этой вкладке выберите Threading model==Single и Interface==Custom. Оставьте остальные установки мастера без изменения и натисните Finish. Наберитесь терпения и дождитесь, когда мастер завершит создание заготовки СОМ объекта.

После работы этого мастера появятся два (основных) новых файла: Mathem.h и Mathem.cpp. Файл Mathem.h содержит объявление сокласса CMathem, включающего интерфейс «по умолчанию» IMathem и пустой конструктор класса. Файл реализации класса Mathem.cpp вообще пока будет пустым. Обратите внимание на «родителей» класса CMathem (файл Mathem.h): вам понятно, кто они по происхождению? Что указано в угловых скобках?

Таким образом, мы имеем заготовку сервера и (одного) СОМ-объекта в нем. Проект можно компилировать и собрать, получить библиотеку DLL (внутрипроцессный сервер), но никакого полезного кода он пока не содержит. Надо собрать волю в кулак, напрячь (или напрясть?) свой могучий интеллект, объявить и реализовать какие-либо методы (и/или свойства) СОМ объекта.

Шаг 3. Добавление метода в СОМ объект.

Для добавления метода надо выбрать вкладку ClassView в окне решений, в ней выбрать ветвь DLLMathServer (а не DLLMathServerPS) и в ней первую подветвь IMathem, помеченную значком интерфейса Часть 1. Разработка простого внутрипроцессного сервера СОМ, реализованного в виде библиотеки DLL - student2.ru . Вызвав контекстное меню для IMathem, выберите команду AddèAdd method для добавления метода к интерфейсу IMathem. В появившемся окне (рис. 2) надо объявить метод. Допустим, что мы хотим реализовать такой ценный (или бесценный?) метод:

HRESULT Cube(double Arg, double *Res)

{

*Res=Arg*Arg*Arg;

return S_OK;

}

Метод может возвращать значение только типа HRESULT (целое 32-битовое число, содержащее результат выполнения метода – код ошибки), а практический результат, в данном случае куб аргумента, мы будем возвращать посредством параметра-указателя Res.

Часть 1. Разработка простого внутрипроцессного сервера СОМ, реализованного в виде библиотеки DLL - student2.ru

Рис. 2. Окно мастера добавления метода в интерфейс

Тогда в окне мастера добавления метода мы должны выполнить такие действия:

· в поле Method name ввести имя метода Cube;

· включить флажок in (входной параметр) в Parameter attributes;

· в списке Parameter type выбрать имя типа DOUBLE;

· в поле Parameter name ввести имя первого параметра функции Arg;

· нажать кнопку Add, завершив тем самым ввод объявления первого параметра функции;

· в списке Parameter type выбрать имя типа второго параметра DOUBLE*;

· включить флажок out (выходной параметр) в Parameter attributes;

· в поле Parameter name ввести имя второго параметра функции Res;

· шлепнуть по кнопке Add.

В результате этих манипуляций окно мастера должно приобрести вид, который и представлен на рис. 2. На вкладке IDL Attributes ничего изменять не нужно. Конечно, не мешало бы ввести информативное описание метода в поле helpstring перед ударом по кнопке Finish (описание можно и не вводить, если у вас есть чудодейственное зелье от проклятий пользователя метода). Подробное описание атрибутов метода имеется в MSDN и вы сможете его улицезреть, если нажмете клавишу F1 или символ ? в правом верхнем углу окна мастера.

Объявление метода будет шустренько добавлено в тело класса CMathem (файл Mathem.h), а заготовка для его определения – в файл реализации Mathem.cpp, куда вы и должны добавить тело метода, приведенное выше. В результате определение метода должно принять такой законченный вид:

STDMETHODIMP CMathem::Cube(DOUBLE Arg, DOUBLE* Res)

{

*Res=Arg*Arg*Arg;

return S_OK;

}

Если побродить по заголовочным файлам, то можно обнаружить, что макрос STDMETHODIMP в конечном итоге приводит заголовок метода к такому виду:

long __stdcall CMathem::Cube(DOUBLE Arg, DOUBLE* Res)

Настоятельно рекомендую ознакомиться с содержимым файла DLLMathServer.idl, который содержит практически полное описание нашего СОМ объекта на Interface Definition Language (IDL) – языке описания интерфейсов. Сие значит, что там объявлен интерфейс IMathem и предоставляемый им пока единственный метод Cube().

Выполните компиляцию и сборку приложения, в результате чего в каталоге Debug (при установке отладочной конфигурации в качестве активной) должен появиться файл DLLMathServer.dll, который и будет содержать земную материализацию внутрипроцессного сервера.

Замечание 1. Ежели при компиляции здесь и далее пред Вашим ясным взором вдруг возникнет окно с сообщением, содержащим текст «This file has been modified outside of the source editor. Do you want to reload it?» – поверьте ИС и шмякните по кнопке «Yes to All»

Если вы обратитесь к анализу содержимого реестра Windows, то уже на этом этапе обнаружите там информацию о вашем сервере (например, поищите имя DLLMathServer.Mathem).

Замечание 2. Для регистрации сервера в реестре у вас должны быть права на запись в разделы реестра HKEY_CLASSES_ROOT и HKEY_LOCAL_MACHINE. Зарегистрировать сервер в реестре можно также с помощью команды «RegSvr32.exe имя_сервера», где имя_сервера должно быть полным именем приложения, включающим путь. Разрегистрировать приложение можно с помощью команды «RegSvr32.exe /u имя_сервера».

Итак, на сием этапе мы получили СОМ объект с пока еще одним интерфейсом IMathem. Обратите внимание также на внешнее имя программы (библиотеки) DLLMathServer.Mathem, указанное в окне Prog ID (см. рис.1), которое нам понадобится при разработке клиентского приложения. Это имя, к слову сказать, указывается в реестре Windows как значение ключа VersionIndependentProgID, а значением ключа ProgID будет DLLMathServer.Mathem.1. Более подробные сведения о том, какие записи в реестре появляются при регистрации сервера, см. в конспекте (подразд. «О регистрации внутрипроцессного сервера СОМ» раздела «Приложения»).

Подведем промежуточные итоги. На этой стадии разработки проекта создана DLL-библиотека, которая является «домом» для нашего СОМ-объекта, и сам СОМ-объект, который имеет один интерфейс и один метод. В файле определения модуля DLLMathServer.def перечислены функции, которые экспортирует наша библиотека. Расслабтесь: вам не придется заниматься реализацией этих пяти стандартных для СОМ-серверов функций, хотя делать это и не запрещено.

Шаг 4.Добавление в СОМ-объект еще одного интерфейса.

Расслабились? Теперь сосредоточтесь. Для добавления нового интерфейса к существующему СОМ объекту необходимо открыть файл библиотеки типов (DLLMathServer.idl) и скопировать описание первого интерфейса, т.е. создать копию следующих строк файла DLLMathServer.idl:

[

Object,

Uuid(2E4FB046-98D8-4E74-8572-84D7133FFEF4),

pointer_default(unique)

]

interface IMathem : IUnknown{

[] HRESULT Cube([in] DOUBLE Arg, [out] DOUBLE* Res);

};

Эти скопированные строки надо разместить непосредственно ниже оригинальных.

Далее необходимо изменить имя интерфейса (в данном примере я выбрал имя IMathem2), удалить строку с описанием метода Cube и заменить идентификатор uuid на новый, воспользовавшись командой ToolsèCreate Guid. В окне мастера генерации uuidпроще всего выбрать четвертый формат представления уникального идентификатора (Registry uuid). Кроме того, новый интерфейс необходимо добавить в описание сокласса (coclass Mathem), но уже без спецификатора default, так как только один интерфейс может быть интерфейсом по умолчанию. В результате этих действий содержимое файла библиотеки DLLMathServer.idl должно стать таким (комментарии в начале файла опущены, а добавленные строки выделены цветом и комментариями. Копировать текст нельзя, так как идентификаторы uuid должны быть уникальными):

import "oaidl.idl";

import "ocidl.idl";

[

Object,

Uuid(2E4FB046-98D8-4E74-8572-84D7133FFEF4),

pointer_default(unique)

]

interface IMathem : IUnknown{

[] HRESULT Cube([in] DOUBLE Arg, [out] DOUBLE* Res);

};

// начало добавлений

[

Object,

Наши рекомендации