Файл: Технология СОМ (Создание и повторное применение объектов СОМ. Маршалинг).pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 25.06.2023

Просмотров: 163

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Запрашивая создание объекта, клиент передает библиотеке СОМ идентификатор класса данного объекта, используя который, библиотека должна найти сервер этого класса. Понятно, что необходима некоторая таблица, отображающая CLSID в местоположение исполняемого кода сервера. В случае Windows эта таблица содержится в системном реестре. (Registry), другие реализации СОМ могут использовать другие схемы. Добавление записей к этой таблице обычно происходит при установке СОМ серверов.

Создание одного объекта. Для создания одного неинициализированного экземпляра объекта СОМ в локальном или внутризадачном сервере выполняются следующие действия (рис. 1):

  1. Клиент вызывает функцию библиотеки COM CoCreateInstance. Кроме других параметров, при вызове задаются CLSID объекта, который должен быть создан, а также IID некоторого интерфейса, поддерживаемого объектом.
  2. Библиотека COM no CLSID находит в системном реестре запись, соответствующую классу данного объекта. Эта запись содержит информацию о местоположении сервера, способного создать экземпляр класса объекта.
  3. Сервер запускается - это может быть внутризадачный или локальный сервер.
  4. Запущенный сервер создает экземпляр класса объекта и возвращает указатель на запрошенный интерфейс библиотеке СОМ.
  5. Библиотека СОМ передает указатель (6) клиенту.

В случае удаленного сервера процесс подобен, но вместо простого запуска сервера библиотека СОМ устанавливает связь с удаленной машиной, запрос выполняется вызовом удаленной процедуры. Просмотрев свой реестр удаленная система находит исполняемый файл сервера и создает экземпляр объекта. В случае, если объект реализован в динамической библиотеке, запускается суррогатный процесс. Далее также, как и в предыдущем случае возвращается указатель на интерфейс.

Таким образом, для клиента запуск объекта выполняется одинаково для всех трех типов серверов.

Рис. 1. Создание объекта с помощью CoCreateInstance

Фабрики классов. В сервере СОМ должны присутствовать средства, обеспечивающие создание экземпляров компонентов по запросу клиента. Для этого используется стандартный интерфейс ICIassFactory. Как и все СОМ-интерфейсы он наследует от IUnknown и, кроме этого, включает в себя два метода: CreateInstance и LockServer. Первый из них создает экземпляр класса (например с помощью оператора new), а второй обеспечивает блокировку программы-сервера в памяти.

Фабрика классов представляет собой СОМ объект, единственной задачей которого является создание других СОМ объектов одного класса. Всякое хранилище компонента, будь то исполняемый или DLL файл, должно обеспечивать реализацию фабрики классов для каждого класса, который может быть создан по внешним запросам.


Фабрика классов необходима для каждого компонента, а хранилище компонента должно обеспечить для библиотеки СОМ способ доступа к этой фабрике. В зависимости от варианта хранения используется одна из двух основных технологий доступа. DLL-файлы должны предоставлять в общее пользование две функции: DllGetClassObject и DllCanUnloadNow, а исполняемые файлы должны регистрировать свои фабрики классов с помощью функции CoRegisterClassObject из библиотеки СОМ.

При создании объектов с помощью фабрики классов выполняются следующие действия (рис. 2):

  1. Чтобы получить доступ к фабрике класса, клиент вызывает функцию библиотеки COM CoGetClassObject. Этой функции передается CLSID класса объектов, которые будет создавать фабрика и IID интерфейса, нужного ему для работы с фабрикой (обычно IClassFactory) а также тип сервера.
  2. Выполняется поиск и запуск СОМ сервера.
  3. Возвращается указатель на интерфейс IClassFactory созданной фабрики класса.
  4. Далее клиент может создать столько экземпляров класса, сколько необходимо, вызывая метод CreateInstance интерфейса IClassFactory.
  5. После этого следует освободить интерфейс IClassFactory, вызвав его метод Release.

Рис. 2. Создание объекта с помощью фабрики классов

Аналогичные действия происходят при вызове функции библиотеки СОМ CoCreateInstance при создании одного объекта:

CoGetClassObject(... ,&pCF);

рСF->CreateInstance(... ,&pint);

pCF->Release();

Иницшииюцин объектов COM. Фабрика классов создает абстрактный экземпляр данного класса. Его необходимо проинициализировать. Для этого его данные должны где-то храниться и наиболее очевидное место для этого - диск. Первый интерфейс, запрашиваемый клиентом при создании объекта обычно является одним из тех, которые позволяют проинициализировать объект, например IPersistFile, IPersistStorage, IPersistStrearn.

Пример реализации внутризадачного СОМ сервера. В примере будет реализован СОМ сервер для объектов одного класса, поддерживающего помимо интерфейса IUnknown единственный собственный интерфейс INewInterface. Язык реализации примера C++.

Заголовочный файл .h будет содержать:

1. Описание абстрактных классов для собственных интерфейсов:

class INewInterface : public IUnknown

{

public

virtual HRESULT _stdcall Method 1() = О; //абстрактный

}

2. Описание констант GUID

3. Описание класса, реализующего интерфейсы:

class NewClass : public INewInterface

{

protected

//счетчик ссылок

public

//конструктор, деструктор

NewClass();

~NewClass();

//виртуально перекрытые методы интерфейса IUnknown


//Querylntertace, AddRef, Release

//виртуально перекрытый метод собственных интерфейсов

virtual НRESULT Method 1 ();

}

4. Класс, реализующий фабрику классов:

class NewClassFactory

{

protected

//счетчик ссылок

public

//конструктор, деструктор

NewClassFactory();

~NewClassFactory();

//виртуально перекрытые методы интерфейса IUnknown

//Querylntertace, AddRef, Release

//виртуально перекрытые методы фабрики классов

//СreateInstance и LockServer

}

Файл .срр будет содержать:

1. Реализацию методов класса NewClass:

1.1. Конструктор

- увеличение внешнего счетчика объектов на I

- обнуление счетчика ссылок

1.2. Деструктор

- уменьшение внешнего счетчика объектов на 1

1.3. Querylntertace: проверка того, поддерживает ли класс запрашиваемый интерфейс, если да, то вызов AddRef. Возвращает указатель this, приведенный к типу указатель на нужный интерфейс (INewInterface *) this;

1.4. AddRef-увеличивает и возвращает счетчик ссылок;

1.5. Release - уменьшает счетчик ссылок. Если он равен 0, удаляет себя delete this.

1.6. Реализация методов собственных интерфейсов.

2. Реализацию методов класса NewClass:

2.1. Конструктор

- увеличение внешнего счетчика объектов на I

- обнуление счетчика ссылок

2.2. Деструктор

- уменьшение внешнего счетчика объектов на 1

2.3. Querylnterface: проверка того, поддерживает ли класс запрашиваемый интерфейс, если да, то вызов AddRef. Возвращает указатель this, приведенный к типу указатель на нужный интерфейс (INewInterface *) this;

2.4. AddRef - увеличивает и возвращает счетчик ссылок

2.5. Release - уменьшает счетчик ссылок. Если он равен 0, удаляет себя delete this.

2.6. Реализация метода CreateInstance интерфейса IClassFactory.

- создаем экземпляр класса, соответствующего классу фабрики

- вызываем его метод Query Interface

2.7. Реализация метода LockServer интерфейса IClassFactory:

- если передано значение «истина» - увеличиваем внешний счетчик обращений на I, иначе-уменьшаем.

3. Внешний счетчик объектов и внешний счетчик обращений (при начальном присваивании = 0)

4. Процедура динамической библиотеки

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)

{

если CLSID соответствует

- создается экземпляр фабрики классов,

- вызывается ее QueryInterface

}

5. Процедура динамической библиотеки STDAPI DllCanUnIoadNow(void)

{

если внешний счетчик объектов или внешний счетчик ссылок не равны 0, возвращает «ложь» - выгрузить нельзя, иначе «истину» - можно.

}

После компиляции можно зарегистрировать внутризадачный СОМ сервер в системном реестре и использовать соответствующие СОМ объекты, например, следующим образом:


CoInitializeO- инициализация библиотеки СОМ

CoGetClassObject(..,&pCF,..) - получили указатель на интерфейс IclassFactory (pCF)

pCF->CreateInstance(...) - создали экземпляр нужного объекта и получили нужный интерфейс

pCF->Release() - освободили фабрику классов //работаем

//освобождаем все интерфейсы

CoUnInitialize() - освобождаем библиотеку СОМ.

2.2. Повторное применение объектов СОМ.

Рассмотрим возможность повторного применения уже существующего кода в новых объектах СОМ.

В объектно-ориентированном программировании в качестве механизма повторного использования существующего кода применяется механизм наследования реализации. Но в СОМ механизма наследования реализации не предусмотрено, предусмотрено только наследование интерфейсов. Поэтому СОМ предоставляет два других механизма повторного применения: включение и агрегирование.

Под включением (containment), которое также называется делегированием (delegation), внешний объект выступает как обычный клиент внутреннего. Внешний объект вызывает методы внутреннего объекта для выполнения собственных функций. Однако эти методы остаются недоступными клиенту внешнего объекта непосредственно. Интерфейс внешнего объекта содержит методы, вызывающие методы внутреннего (рис. 3).

Реализация включения столь же проста, сколь проста реализация клиента СОМ и является по этому широко распространенным механизмом. Но оно не всегда является самым эффективным механизмом.

Рис. З. Включение

Предположим, объекту нужно предоставлять клиентам некоторый интерфейс, и уже имеется другой объект, который этот интерфейс реализует. Внешний объект мог бы реализовать все методы данного интерфейса так, чтобы они не делали ничего, кроме вызовов соответствующих методов внутреннего объекта, который, в свою очередь, возможно, переадресует вызовы к следующему объекту и т.д. что не эффективно. В этом случае используется механизм повторного применения, называемый агрегацией, (aggregation).

Механизм агрегации позволяет внешнему объекту представлять в качестве собственных интерфейсы, реализованные на самом деле внутренними объектами. Агрегирование повышает эффективность и, как и включение, не видимо для клиента. Но, в отличие от включения, агрегирование требует поддержки со стороны внутреннего объекта.


Имеются две проблемы:

- клиент может вызвать метод Querylnterface внутреннего объекта, чтобы получить указатель на один из интерфейсов внешнего, который об этом ничего не знает;

- клиент может вызвать метод AddRef внутреннего объекта, а внешний об этом ничего не узнает.

Решение этих проблем очевидно: любой внутренний объект должен делегировать вызовы методов своего IUnknown методам IUnknown внешнего объекта. Следовательно, внутреннему объекту надо как-то передать указатель на интерфейс IUnknown внешнего. Эгот указатель передается как параметр либо CoCreatelnstance, либо ICIassFactory::CreateInstance при создании агрегируемого объекта. Если соответствующий параметр NULL, то объект знает, что он не агрегируется, и будет обрабатывать все вызовы методов IUnknown самостоятельно. Если не NULL, новый объект будет функционировать только как агрегированный внутренний объект некоторого объекта и вызовы методов IUnknown внутреннего объекта будут делегироваться методам IUnknown внешнего объекта.

Рис.4. Агрегирование

2.3. Маршалинг (транспортировка)

Существует три вида СОМ серверов, но во всех случаях клиент выбывает методы их интерфейсов одинаков - напрямую. Рассмотрим, какие действия при этом происходят.

Простейший случай, когда сервер реализован в процессе. Указатель интерфейса в этом случае может указывать непосредственно на таблицу виртуальных методов. Вызов метода интерфейса клиентом представляет собой обычный вызов виртуального метода объекта. Вызовы СОМ-объекта, реализованного внутризадачным сервером столь же эффективны, как и вызовы функций C++ в том же процессе (рис.5 ).

Рис.5. Доступ к объекту СОМ, реализованному сервером «в процессе»

Случай реализации объекта СОМ в локальном сервере несколько сложнее. Так как клиентский интерфейс не может указывать непосредственно на интерфейс в объекте, он указывает на заместитель (proxy) внутри клиентского процесса. Заместитель - это просто другой COM-объект (в процессе - .DLL), обычно предоставляющий клиенту те же интерфейсы, что объект в локальном сервере. Вызов клиентом метода через такой указатель на интерфейс на самом деле вызывает исполнение кода заместителя. Заместитель принимает переданные клиентом параметры и упаковывает их для пересылки. Затем с помощью некоего средства коммуникации между процессами (зависит от ОС) передает запрос и его параметры в процесс, который фактически реализует объект.