Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2920

.pdf
Скачиваний:
1
Добавлен:
15.11.2022
Размер:
2.61 Mб
Скачать

ShowMessage('Summa(1, 2) = ' +

IntToStr(VCTestObj1.Summa(1, 2)));

VCTestObj1.Disconnect();

Еще одна полезная черта «оберток» заключается в том, что они преобразуют обработку ошибок «в стиле COM» в схему, принятую для базового языка.

Еще меньше приходится делать в VB. В нем надо только зарегистрировать ссылку, объявить переменную и вызвать метод. Если впоследствии в библиотеке, содержащей вызываемый объект, произойдут изменения, VB сам их отследит. Вот код вызова метода нашего тестового компонента:

Dim obj As New VCTestObj

MsgBox "Summa(1, 2) = " & obj.Summa(1, 2)

В приведенных выше примерах предполагается, что сервер соответствующим образом зарегистрирован. По умолчанию DLL-библиотека, в которую помещен COMобъект, регистрируется как внутрипроцессный (in-process) сервер. Это приводит к тому, что DLL будет грузиться внутрь каждого процесса, который попытается создать находящийся в ней объект. Чтобы с объектами из этой DLL-библиотеки можно было работать по сети, необходимо зарегистрировать ее в COM+ или MTS. Вот как выглядит эта операция.

Первое что необходимо сделать, это открыть COM+ Component Services (MTS Explorer) и добраться до папки «My Computer». В ней необходимо выбрать папку «COM+ Applications» и создать (или открыть, если оно уже существует) новое приложение COM+. Создать новое приложение можно, выбрав из меню «Action» (или из контекстного меню) пункт «New\Applications».

В одно приложение можно помещать компоненты из разных библиотек. Приложение олицетворяет процесс, в котором будут загружаться и исполняться компоненты. Если есть необходимость загружать компоненты в разных

111

процессах, то нужно помещать их в разные приложения

COM+.

COM+ Component Services (для Windows NT 4.0 – MTS Explorer) позволяют не только производить первичную регистрацию компонентов, но и дают возможность настраивать их характеристики, производить отладку компонентов, осуществлять их мониторинг и даже создавать инсталляторы для распределенных приложений.

Чтобы зарегистрировать приложение COM+ на клиентской машине, необходимо с помощью COM+ Component Services (MTS Explorer) создать proxy-инсталлятор.

Затем нужно прогнать этот инсталлятор на каждой клиентской машине. Деинсталляция COM+-приложения, как и любого другого Windows-приложения, доступна через «Control Panel»

– «Add\Remove Programs».

Не-СОМ+-приложения можно зарегистрировать и администрировать с помощью утилиты DCOMCNFG.

2.4.2. Управление объектами

С помощью COM можно создавать объекты, реализация которых состоит из нескольких других объектов. Такой составной объект выглядит как единый. Агрегация – лучший в COM-модели способ повторного использования бинарных компонентов, не приводящий к потерям производительности. Его слабой стороной является то, что нельзя перехватывать и по-своему обрабатывать отдельные методы интерфейсов агрегируемых объектов. Можно только подставлять свою реализацию целого интерфейса.

Использовать функциональность других компонентов, но не давать им при этом свободы действий, можно с помощью делегации.

Это простое включение объекта внутрь и передача (делегация) ему для обработки некоторых вызовов другого объекта. Этот способ обеспечивает наибольшую гибкость, но

112

проигрывает по эффективности агрегации, так как производятся лишние вызовы.

Время жизни COM-объекта ограничено временем жизни серверного приложения, в котором создан этот объект. К тому же объект самоуничтожается при освобождении (вызовом метода Release) последней ссылки на него. Тем не менее, на практике это не приводит к каким-нибудь ограничениям. Если логическое время жизни объекта должно превышать время жизни экземпляра объекта в оперативной памяти, можно просто сохранить в файл или в поле БД его состояние перед уничтожением, а когда он понадобится, загрузить его вновь. Сохраненный объект может путешествовать между компьютерами, например, через параметр удаленного объекта или в электронном письме. На нужном компьютере надо просто создать новый экземпляр нужного класса и инициализировать его состояние пришедшими данными.

Сохранение и загрузку объектов можно инкапсулировать в методах другого удаленного объекта. Например, можно создать объект с методами Save и Load, передавая им в качестве параметров идентификатор БД или имя файла и указатель на интерфейс сохраняемого (загружаемого) объекта. Часто для этого применяются моникеры. OLE DB и ADO позволяют записывать в БД объекты, поддерживающие стандартные интерфейсы IPersistXXX (см. «Инициализация и сохранение объектов»).

COM+ даже позволяет вызывать методы объектов на выключенном компьютере. Эти возможности освещается в разделе «Асинхронное взаимодействие между объектами».

При вызове CoCreateInstance вы не обязательно получаете указатель на новый объект. Вполне возможно, что вы получите указатель на уже созданный, разделяемый объект. Такой объект называется singleton. За то, как выполняется операция создания экземпляра COM-объекта, отвечает так называемая фабрика классов. Ее реализация находится в том

113

же модуле, что и реализация объектов создаваемых этой фабрикой.

Если для создания COM-объектов вы используете ATL, то для реализации singleton-объекта надо добавить одну строчку в код вашего объекта:

DECLARE_CLASSFACTORY_SINGLETON(CMyClass)

где CMyClass это имя вашего класса.

Singleton-объекты удобны, когда необходимо организовать управление общими ресурсами. Такие объекты похожи на Corba-объекты, использующие сервант по умолчанию.

В COM не реализован сервис имен (Naming Services), аналогичный CORBA. Этот Corba-сервис позволяет получать ссылку на конкретный экземпляр объекта, созданный на сервере. Идентификация объекта при этом происходит по имени.

Обычно необходимость такой функциональности встречается редко, так как COM придерживается отличных от Corba принципов взаимодействия с объектами. Но если такая функциональность вам необходима, то можно создать свой сервис имен. Для этого удобно воспользоваться функциональностью Singleton-объектов. Singleton-объект создает другие объекты и помещает их в массив, статический в простом случае, и динамический, или мап (объект, позволяющий ассоциировать одни значения с другими – в нашем случае имена объектов с указателями на их интерфейс) в более сложном случае. Один из методов этого Singletonобъекта принимает в качестве входного параметра имя и возвращает ассоциированный с ним объект.

Если объект реализован в DLL, то, в принципе, его экземпляры можно создавать внутри нескольких EXEсерверов, но регистрировать в COM+ или MTS их можно только один раз (на одной машине). Если есть необходимость создавать такие COM-объекты в разных EXE-модулях, то их или вовсе не надо регистрировать в COM+/MTS, или нужно

114

регистрировать как библиотечное приложение (библиотечный пакет в MTS). При этом создание экземпляров и передачу указателей на их интерфейсы необходимо делегировать другим объектам, зарегистрированным в COM+/MTS.

Если создается монолитный EXE-сервер, то тем, будет или нет загружаться новая копия EXE-модуля при создании нового объекта (и некоторыми другими аспектами), можно управлять через один из параметров функции CoRegisterClassObject. ATL использует эту функцию при загрузке EXE-модуля для регистрации реализуемых им компонентов.

2.4.3. Массивы

Для передачи по сети массивов MIDL использует атрибуты size_is() и length_is(). При этом метод, передающий массив, будет выглядеть так:

HRESULT Method1( [in] short iLength,

[in, length_is(iLength)] short asNumbers[10]); HRESULT Method2(

[in] short iLength, [in] short iSize,

[in, length_is(iLength), size_is(iSize)] short * asNumbers);

Атрибут size_is определяет, сколько памяти будет занято RPC-заглушкой, а length_is определяет, сколько данных надо реально передавать. Так, в первом методе на вызываемой (так как все параметры типа "in") стороне всегда будет заниматься память для десяти элементов (10 * sizeof(short) = 20 байт) массива, но передаваться по сети будет iLength элементов. Во втором случае динамически будут определяться оба параметра. Если объем занимаемой памяти равен передаваемому, то можно пользоваться только атрибутом size_is. В работе с чисто входными (in) и чисто выходными параметрами (out) есть несколько нюансов, но рассказ об этом может занять слишком много места.

115

MIDL позволяет использовать заканчивающиеся нулем строки. Правила работы со строками те же, что и в C/C++. Поддерживаются как обычные, так и двухбайтные строки. Чтобы сообщить MIDL, что некоторый параметр рассматривается, как строка, нужно пометить его специальным атрибутом – string. VB не поддерживает этот атрибут. Для работы со строками в нем применяется специальный тип –

BSTR. Используя атрибуты size_is() и length_is(), можно передавать строки, содержащие в своем теле несколько NULLсимволов.

Еще одной интересной особенностью MIDL является поддержка им объединений (union), аналогичных объединениям в С++. Расширенный атрибут switch_is позволяет задать переменную, используемую для определения элемента объединения, имеющего смысл в данный момент времени. Обычно объединение (вернее, его описание и переменную, созданную на базе этого объединения) помещают в структуру. Одно из полей этой структуры и используется в атрибуте switch_is.

Самым радикальным случаем при работе с MIDL является так называемый wire marshaling. Этот тип маршалинга преобразует сложный тип в простой с помощью пользовательских функций. Суть его вкратце можно описать так. У вас есть тип данных, который по каким-то причинам нельзя переслать по сети в качестве параметра функции или в составе другого типа данных. Примером может служить указатель на объект C++ или тип данных, содержащий ссылки на локальные ресурсы. В этом случае вы объявляете этот тип в

IDL с помощью атрибута wire_marshal или user_marshal. При этом вы задаете исходный тип и так называемый wire-тип. Wire-тип – это некоторый базовый тип, который априори может передаваться по сети, например, массив байтов. Далее надо реализовать четыре функции:

116

_UserSize – Вычисляет размер, необходимый для буфера данных RPC. Этот метод вызывается перед передачей данных на другой компьютер.

_UserMarshal – Запаковывает данные в Wire-тип.

_UserUnmarshal – Распаковывает данные из Wire-

типа.

_UserFree – Освобождает память, занятую при распаковке. Вызывается RPC-заглушкой на стороне клиента, позволяя тем самым освободить данные, на которые ссылается исходный тип.

На основе IDL-описания и кода этих функций MIDLкомпилятор создает специальную заглушку, используемую RPC-подсистемой ОС для передачи по сети того, что иначе не передается.

Листинг 1. IDL-файл примера «работа со структурами и массивами»

//RemTest1.idl : IDL source for RemTest1.dll

//

//Этот файл будет обработан MIDL для

//создания библиотеки типов (RemTest1.tlb) и кода маршаллинга.

import "oaidl.idl"; import "ocidl.idl";

[

uuid(421D531D-A54F-4137-93E8-7006448611B7), version(1.0),

helpstring("RemTest1 1.0 Type Library")

]

library REMTEST1Lib

{

importlib("stdole32.tlb");

importlib("stdole2.tlb");

117

typedef [uuid(7EF6E5F1-ACF8-49a5-B49B-A92349F27157), version(1.0)]

enum SomeEnum

{

Some,

Some1,

Some2,

Some3

}SomeEnum;

typedef [uuid(080C357D-EE8D-40b0-AD0A-5667995BB186), version(1.0)]

struct SomeStruct

{

int i; BSTR str;

SomeEnum se; }SomeStruct;

[

object, uuid(153450BE-9F71-4193-9A00-95C58175FD8F), version(1.0),

dual,

nonextensible, pointer_default(unique), oleautomation

]

interface IO : IDispatch

{

[id(1)] HRESULT M1([in,out] SomeStruct * ss);

[id(2)] HRESULT M2([in,out] SAFEARRAY(SomeStruct) * ssbs);

[id(3)] HRESULT M3([in] long len, [in, size_is(len)] SomeStruct * ssa);

118

};

[uuid(51ABF99E-4583-4DF1-85F5-51DE0EB57CDC)] coclass O

{

[default] interface IO;

};

};

Листинг 2. Заголовочный файл примера «работа со структурами и массивами»

// O.h : Declaration of the CO #ifndef __O_H_

#define __O_H_

#include "resource.h" // main symbols

/////////////////////////////////////////////////////////////////////////////

// CO

class ATL_NO_VTABLE CO :

public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CO, &CLSID_O>,

public ISupportErrorInfo,

public IDispatchImpl<IO, &IID_IO, &LIBID_REMTEST1Lib>

{

public:

CO(){}

DECLARE_REGISTRY_RESOURCEID(IDR_O) DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CO)

COM_INTERFACE_ENTRY(IO)

COM_INTERFACE_ENTRY(IDispatch)

COM_INTERFACE_ENTRY(ISupportErrorInfo)

119

END_COM_MAP()

//ISupportsErrorInfo STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

//IO

public:

STDMETHOD(M3)(/*[in]*/ long len, /*[in, size_is(len)]*/ SomeStruct * ssa);

STDMETHOD(M2)(/*[in,out]*/ SAFEARRAY ** ssbs); STDMETHOD(M1)(/*[in,out]*/ SomeStruct * ss);

};

#endif //__O_H_

2.4.4. Высокоуровневый вариант (Automation/VB)

Концепция этого варианта очень проста: вы не можете использовать низкоуровневые средства MIDL. Вместо этого вы используете стандартные (их еще называют Automationсовместимыми) типы данных, структуры и перечисления. VB делает это автоматически. Ниже приведено краткое описание Automation-совместимых типов:

VARIANT_BOOL – Логический тип. Может иметь значение True (-1) или False (0).

BYTE – 8-битное целое число без знака.

int – Целое число со знаком. На 32-хбитных платформах имеет размер 32-бита.

long – 32-битное целое число со знаком.

short – 16-битное целое число со знаком.

double – 64-битное число с плавающей точкой.

float – 32-битное число с плавающей точкой.

CURRENCY – 64-битное число, с фиксированной

точкой.

120

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]