Разработка сервера
Сейчас мы займемся разработкой DLL СОМ-сервера, выполняемого в пространстве процесса другого (клиентского) приложения. Для того чтобы понять, что кроется за этой вывеской, мы для начала создадим минимально-простой СОМ-объект и при этом специально не будем пользоваться какими-либо библиотеками или инструментами Studio.Net.Наш объект будет предоставлять миру только один интерфейс isay, инкапсулирующий два метода: Say и SetWord. Первый метод выводит текстовую строку типа BSTR в окно типа MessageBox, а второй — позволяет изменять эту строку. Тип BSTR в Win32 является адресом двухбайтовой Unicode-строки. Его советуют использовать в СОМ-объектах для обеспечения совместимости с клиентскими приложениями, написанными на других языках.
Я надеюсь, что логика, заложенная в этом простом приложении, поможет вам не терять нить повествования при разработке следующего, более сложного объекта с помощью ATL. Использование ATL и инструментов Studio.Net упрощают разработку СОМ-объектов, но скрывают суть происходящего, вызывая иногда чувство досады и неудовлетворенности. С помощью мастера AppWizard создайте шаблон приложения типа Win32 Dynamic-Link Library (Динамически компонуемая библиотека Win32) под именем МуСот.
- Дайте команду File >
New * Project. В диалоге New Project выберите шаблон Win32 Project
под именем МуСот и нажмите ОК.
- В окне Win32 Application
Wizard откройте вкладку Application Settings, установите переключатель Application
Type в положение DLL, включите флажок Empty Project и нажмите кнопку Finish.
- Подключите к проекту
новый файл типа C/C++ Header File. Для этого дайте команду Project > Add
' New Item. В диалоге Add New Item выберите шаблон Header File (.h), а в поле
Name задайте имя interfaces.h и нажмите кнопку Open
- Введите в этот файл нижеследующие
директивы препроцессора и описание интерфейса ISay.
//=== Эти директивы нужны для того, чтобы не допустить //=== повторное подключение файлаЭто же действие можно выполнить более сложным способом, но зато сход-ным с тем, как это делалось в Visual Studio 6. Дайте команду File > New > File, выберите тип файла и нажмите кнопку Open. Кроме этих действий придется записать новый файл в папку с проектом и подключить его. Для этого используется команда Project > Add Existing Item с последующим поиском файла. Альтернативой этому является перетаскивание существующего файла в окне Solution Explorer из папки Resource Files в папку Header Files.
#if !defined(MY_ISAY_INTERFACE)
#define MY__ISAY_INTERFACE #pragma once
//====== Для того, чтобы были доступны COM API
#include <windows.h>
//====== Для того, чтобы был виден lUnknown
#include <initguid.h>
// Интерфейс ISay мы собираемся зарегистрировать и // показать миру. Он, как и положено, происходит от // IUnknown и содержит чисто виртуальные функции
interface ISay : public lUnknown {
//=== 2 метода, которые интерфейс
//=== предоставляет своим клиентам
virtual HRESULT _stdcall Say 0=0;
virtual HRESULT _stdcall SetWord (BSTR word)=0; }
#endif
Абстрактный интерфейс не может жить сам по себе. Он должен иметь класс-оболочку (wrapper class), который на деле реализует виртуальные методы Say и SetWord. Этот так называемый ко-класс (класс СОМ-компонента) производится от интерфейса ISay и предоставляет тела всем унаследованным (чисто) виртуальным методам своего родителя. Так как у интерфейса ISay, в свою очередь, имеется родитель (lUnknown), то класс должен также дать реальные тела всем трем методам IUnknown.
Примечание
Для того чтобы быть доступным тем приложениям, которые захотят воспользоваться услугами СОМ-объекта, сам класс тоже должен иметь дом (в виде inproc-сервера DLL). Сейчас, разрабатывая проект типа Win32 DLL, мы строим именно этот дом. С помощью механизма DLL класс будет доступен приложению-клиенту, в адресное пространство процесса которого он загружается. Вы знаете, что DLL загружается в пространство клиента только при необходимости.Если вы хотите, чтобы класс реализовывал несколько интерфейсов, то вы должны использовать множественное наследование. Такой подход проповедует ATL (Active Template Library). MFC реализует другой подход к реализации интерфейсов. Он использует вложенные классы. Каждому интерфейсу соответствует новый класс, вложенный в один общий класс СОМ-объекта.
Нам неоднократно понадобятся услуги инструмента Studio.Net под именем GuidGen, поэтому целесообразно ввести в меню Tools (Инструментальные средства) Studio.Net новую команду для его запуска. GuidGen, так же как и UuidGen, умеет генерировать уникальные 128-битовые идентификаторы, но при этом он использует удобный Windows-интерфейс. А идентификаторы понадобятся нам для регистрации сервера и класса CoSay. Для введения новой команды:
- Дайте команду Tools
> External Tools и в окне диалога External Tools нажмите кнопку Add.
- Введите имя новой команды
меню GuidGen, переведите фокус в поле Command и нажмите кнопку справа от нее.
- С помощью диалога поиска
файла, найдите файл Guidgen.exe, который находится в папке .. .\Microsoft
Visual Studio.Net\Common7\Tools, и нажмите кнопку Open.
- Переведите фокус в поле
Initial Directory и с помощью кнопки раскрытия выпадающего списка выберите
элемент Item Directory.
- Нажмите OK и теперь с
помощью новой команды GuidGen меню Tools вызовите генератор уникальных идентификаторов.
- Выберите формат DEFINE_GUID
и нажмите кнопку Сору, а затем Exit.
- В окне редактора Studio.Net
поместите фокус перед строкой interface ISay и нажмите Ctrl+C. При этом из
системного буфера в файл будут помещены три строки кода, которые с точностью
до цифр, которые у вас будут другими, имеют такой вид:
DEFINE_GUID («name»,
0xl70368d0, 0x85be, 0x43af, 0xae, 0x71, 0x5, Ox3f, 0x50,
0x66, 0x57, Oxa2);
Замените аргумент «name» на HD_ISay. Повторите всю процедуру и создайте идентификатор для ко-класса CoSay, который вставьте сразу за идентификатором интерфейса ISay. На сей раз замените аргумент «name» на CLSiD_CoSay, например:
// {9B865820-2FFA-lld5-98B4-OOE0293F01B2}
DEFINE_GUID(CLSID_CoSay,
0х9b865820, 0x2ffa, 0xlldS, 0x98, 0xb4, 0x0, 0xe0, 0x29,
0x3f, 0xl, 0xb2);
Сохраните и закройте файл interfaces.h, так как мы больше не будем вносить в него изменений. Если вы хотите знать, что делает макроподстановка DEFINE_GUID, то за ней стоит такое определение:
#define DEFINE_GUID (name, 1, wl, w2, \ b1, b2, bЗ, b4, b5, b6, b7, b8) \ EXTERN_C const GUID name \
= { 1, wl, w2, { b1, b2, bЗ,b4, b5, b6, b7, b8 } }
Оно означает, что макрос создает структуру с именем <name> типа GUID, которая служит для хранения уникальных идентификаторов СОМ-объектов, интерфейсов, библиотек типов и других реалий причудливого мира СОМ.
Назад | Начало | Вперед |