|
Разработка
сервера
Сейчас мы займемся
разработкой 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.
Примечание
Если вы хотите, чтобы
класс реализовывал несколько интерфейсов, то вы должны использовать множественное
наследование. Такой подход проповедует ATL (Active Template Library). MFC
реализует другой подход к реализации интерфейсов. Он использует вложенные
классы. Каждому интерфейсу соответствует новый класс, вложенный в один общий
класс СОМ-объекта.
Для того чтобы
быть доступным тем приложениям, которые захотят воспользоваться услугами СОМ-объекта,
сам класс тоже должен иметь дом (в виде inproc-сервера DLL). Сейчас, разрабатывая
проект типа Win32 DLL, мы строим именно этот дом. С помощью механизма DLL класс
будет доступен приложению-клиенту, в адресное пространство процесса которого
он загружается. Вы знаете, что DLL загружается в пространство клиента только
при необходимости.
Нам неоднократно
понадобятся услуги инструмента 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. При этом из
системного буфера в файл будут помещены три строки кода, которые с точностью
до цифр, которые у вас будут другими, имеют такой вид:
//
{170368DO-85BE-43af-AE71-053F506657A2}
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, которая служит
для хранения уникальных идентификаторов СОМ-объектов, интерфейсов, библиотек
типов и других реалий причудливого мира СОМ.
| |