Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ППП, СОМ, ОLE.doc
Скачиваний:
7
Добавлен:
22.11.2019
Размер:
345.6 Кб
Скачать

Основы архитектуры сом

Дальнейшее развитие технологии внедрения и связывания получило в стандарте OLE 2.0. Основой этого усовершенствованного подхода явилась компонентная модель объекта (Component Object Model - СОМ), которая определяет способ построения составных объектов (Component Objects) и предоставления доступа к ним, базируясь на объектно-ориентированном подходе. Поэтому, очевидно, что такая модель является спецификацией независимой от языка программирования и предусматривает полную совместимость во взаимодействии между компонентами, написанными разными компаниями и на разных языках.

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

Составной объект является экземпляром некоторого класса и поддерживает определенное количество интерфейсов, обычно не менее двух. Интерфейсы используются для получения клиентом доступа к службам объекта. Методом интерфейса является функция или процедура, которая выполняет некоторое действие и может быть вызвана из приложения, использующего данный объект (такое приложение обычно называется клиентом объекта). Клиенты могут получить доступ к службам объекта СОМ только путем вызова методов интерфейсов объекта (рис. 2.26) и не имеют непосредственного доступа к данным объекта. Каждому такому интерфейсу присваивается уникальный идентификатор.

Рис. 2.26. К понятию технологи COM (компонентной модели объекта)

Серверы СОМ обычно представляет собой исполняемый файл (или динамическую библиотеку), содержащий, по крайней мере, один объект СОМ. Различают три типа серверов:

  • внутренние серверы (in-process), которые являются динамическими библиотеками, подключенными к приложению-клиенту и работающие с ним в одном адресном пространстве;

  • локальные серверы, представляющие собой отдельные приложения, работающие на том же компьютере, что и приложение-клиент. Локальный сервер в отличие от внутреннего функционирует в собственном адресном пространстве. Поэтому такие серверы также называют внешними.

  • удаленные серверы (remote), которые являются приложениями, функционирующими на другом по отношению к клиенту компьютере.

Клиентом составного объекта обычно называется приложение, из которого осуществляется доступ к объекту СОМ.

Примечание: в качестве клиента может выступать не только приложение, но и динамическая библиотека.

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

Рис. 2.27. Локальная схема взаимодействия объектов СОМ

  • клиент запрашивает составной объект;

  • ОС Windows выполняет поиск сервера запрошенного объекта и, в случае если поиск будет успешным, выполнит запуск сервера и вернет приложению-клиенту указатель на запрашиваемый интерфейс.

Таким образом, технология СОМ предоставляет возможность одной программе (клиенту) работать с объектами другой программы (сервера).

ПРИМЕЧАНИЕ. Возможность создания "автоматизированных" серверов OLE - одна из ключевых функций любой системы программирования, будь то MS Developer Studio или Delphi. Она позволяет программистам, использующим RAD, создавать автономные OLE-объекты, которые предоставляют свойства и методы автоматизации клиентам OLE. До недавних пор серверы OLE и программы-клиенты должны были размещаться на одном ПК. Но с введением средств DCOM, OLE Enterprise и Remote Automation они становятся доступными для программ-клиентов, работающих под управлением Windows 3.x, Windows 95 или Windows NT и на сетевых рабочих станциях.

И так, прикладные интерфейсы COM API — это все те же, всем известные OLE-интерфейсы, ведущие свое происхождение от DDE (Dynamic Data Exchange) — динамического обмена данными, позволяющего единственным способом в Windows осуществлять обмен информацией между процессами. Т.е. по сути своей СОМ является простым объектным расширением OLE-технологии, когда в локальной модели средств автоматизации OLE команды от клиента проходят через модуль-инициализатор (proxy), а сообщение — в модуль-переходник (stub), где с помощью процедуры локального вызова (RPC) оно разбирается, анализируется и откуда посылается команда на сервер, как показано на рис. 2.28.

Рис. 2.28. Схема работы OLE Automation

В дистанционной модели инициализатор и переходник используют процедуры дистанционного вызова (RPC) для передачи запросов клиента по сети к модулю Automation Manager, функционирующему на сервере. Automation Manager передает данные клиента указанному OLE-серверу и ответные данные по сети к программе-клиенту (рис. 2.28).

Как можно видеть из приведенных рисунков, замечательным свойством Remote Automation является его прозрачность как для пользователя, так и для программиста. Можно создать и запустить на своей машине сервер OLE, а затем приспособить его для работы в дистанционном режиме простым перенесением OLE-сервера на удаленный ПК, на котором работает модуль-администратор автоматизированного управления Automation Manager. Затем нужно просто зарегистрировать новое местоположение OLE-сервера, используя модуль автоматизации дистанционного управления (например - утилита в VB Remote Automation Connection Manager). Программисту не приходится менять ни одной строки текста программы, чтобы использовать разработанный OLE-сервер на удалении. Сейчас почти в любой RAD-системе имеются шаблоны для создания таких OLE-серверов И поскольку регистрацию удаленного сервера легко включить в процедуру установки программ-клиентов, пользователю не приходится предпринимать каких-либо дополнительных действий, чтобы обзавестись новыми возможностями.

Идентификация объектов СОМ

Каждый интерфейс СОМ характеризуется двумя именами. Одно из них представляет собой просто строку символов. По соглашению строковые имена большинства СОМ-интерфейсов начинаются с буквы I (от слова «interface»). Различные технологии, основанные на компонентной модели, определяют интерфейсы с разными именами, но все они обычно начинаются с буквы I и частично отражают в названии назначение интерфейса. Например, интерфейс, используемый для передачи данных от сервера клиенту, имеет строковое имя IDataObject.

Простые строковые имена интерфейсов удобны при выборе имен переменных и типов для указателей интерфейсов. Однако они не годятся, когда клиент должен точно указать, какой именно интерфейс объекта ему нужен. Не исключена ситуация, когда два разных интерфейса разных объектов имеют одинаковые имена. В этом случае клиент при запросе указателя интерфейса может получить неверную ссылку. Поэтому для идентификации интерфейса системой Windows используется глобальный уникальный идентификатор (GUID — globally unique identifier). Для составных объектов он называется идентификатором класса и для его обозначения используется аббревиатура CLSID (class identifier). GUID для интерфейсов называются идентификаторами интерфейса и обозначаются с аббревиатурой IID (interface identifier)..

Идентификатор GUID представляет собой 16-байтовую величину, которая генерируется автоматически при создании объекта СОМ или интерфейса.

Примечание. При генерации идентификатора GUID необходимо обеспечить его глобальную уникальность. Это достигается обеспечением уникальности генерируемой величины во времени и пространстве. Уникальность во времени обеспечивается за счет того, что каждый GUID содержит метку времени, указывающую, когда он был создан, что гарантирует отличие друг от друга всех GUID, сгенерированных на данной машине. Для обеспечения уникальности в пространстве используется уникальный идентификатор компьютера, который может быть использован для генерации GUID. В качестве такого идентификатора программа генерации GUID обычно использует уникальное значение, уже имеющееся на большинстве компьютеров: адрес сетевой платы. Если в компьютере не установлен сетевой адаптер, то из различных случайных характеристик данной системы генерируется фиктивный идентификатор машины. Но и в этом случае маловероятно, что идентификаторы двух машин окажутся одинаковыми.

Интерфейс (IUnknown)

Базовым интерфейсом всех объектов СОМ является интерфейс IUnknown. Каждый объект СОМ должен поддерживать данный интерфейс, в противном случае он не будет объектом СОМ. Интерфейс IUnknown имеет всего три метода: QueryInterface, AddRef и Release. Так как все другие интерфейсы являются наследниками IUnknown, его методы могут быть вызваны через любой из указателей на интерфейсы объекта. Тем не менее, IUnknown является отдельным самостоятельным интерфейсом с собственным IID, так что клиент может запросить указатель непосредственно на IUnknown.

Так метод QueryInterface возвращает указатель на интерфейс объекта, идентификатор IID которого передается в параметре данного метода. Если запрашивается интерфейс, который не поддерживается объектом, то метод QueryInterface возвращает NULL. Таким образом, имея указатель на один интерфейс, клиент может получить указатели на другие интерфейсы объекта, методы которых ему необходимо вызывать. Именно эта простая схема позволяет решить важную и сложную проблему контроля версий. Использование метода QueryInterface позволяет устанавливать новые версии объектов с расширенными возможностями, не влияя на работу программ, использующих только старые возможности. Кроме того, можно обеспечить работоспособность клиента, разработанного с учетом новых возможностей составного объекта, со старой версией объекта СОМ.

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

ПРИМЕЧАНИЕ. Технология СОМ запрещает вносить изменения в существующие интерфейсы.

То, что объект теперь поддерживает еще и IRegress, совершенно неизвестно старой версии клиента, и, следовательно, он никогда не запросит у объекта указатель на этот интерфейс.

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

Рассмотрим теперь, как при использовании объектов СОМ можно изменять или расширять функциональные возможности существующих интерфейсов. Как уже отмечалось выше, СОМ не разрешает изменять интерфейсы. Поэтому для повышения функциональности существующих интерфейсов (например, интерфейса ISimpleStat) разработчик составного объекта должен определить новый интерфейс (например, ISimpleStat2) и включить в него необходимые новые или измененные методы. Объект попрежнему будет поддерживать старый интерфейс ISimpleStat, но теперь он также будет поддерживать и ISimpleStat2. Добавление в объект поддержки ISimpleStatZ ничем не отличается от добавления поддержки любого нового интерфейса. Как и в предыдущем случае с IRegress, клиенты, ничего не знающие о происшедшей модернизации, никогда не запросят указатель на ISimpleStat2, то есть будут продолжать использовать ISimpleStat точно так же, как и прежде.

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

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

Методы AddRef и Release

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

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

Каждый исполняющийся объект поддерживает счетчик ссылок. Выдавая указатель на один из своих интерфейсов, объект всякий раз увеличивает счетчик ссылок на 1.

ПРИМЕЧАНИЕ. Объект может поддерживать отдельные счетчики ссылок для каждого своего интерфейса.

Если один клиент передает указатель интерфейса другому клиенту, то есть увеличивает число пользователей объекта без ведома последнего, то клиент, получающий указатель, должен вызвать метод AddRef полученного интерфейса. В результате объект увеличивает свой счетчик ссылок.

Независимо от того, как клиент получил указатель на интерфейс, при завершении работы с ним клиент всегда обязан вызвать метод Release. Исполнение этого метода объектом состоит в уменьшении числа ссылок на 1. Обычно объект уничтожает сам себя, когда счетчик ссылок становится равным 0.

ПРИМЕЧАНИЕ. Обеспечение правильности подсчета ссылок целиком лежит на программисте. Если не все клиенты следуют правилам подсчета ссылок, то экземпляр объекта может либо существовать неопределенно долго, либо, что еще хуже, может быть преждевременно удален. Поэтому при написании приложений-клиентов следует быть очень внимательным и обеспечивать правильный подсчет ссылок.

Библиотека СОМ и фабрика класса

Для обеспечения поддержки составных объектов в системе, поддерживающей СОМ, обязательно имеется некоторая реализация библиотеки СОМ. Данная библиотека содержит функции, предоставляющие базовые службы объектам и их клиентам. Более того, библиотека предоставляет клиентам способ запуска серверов объектов. Доступ к службам библиотеки СОМ осуществляется через вызовы обычных функций, а не через методы интерфейсов СОМ-объектов. По соглашению, имена функций библиотеки СОМ начинаются с букв «Со» (например, CoCreateInstance — одна из основных функций библиотеки СОМ, создающая экземпляр составного объекта).

Если клиенту нужен только один составной объект, то легче всего создать его с помощью метода CoCreateInstance. Однако в некоторых случаях клиенту может понадобиться несколько экземпляров объекта одного и того же класса. В этом случае следует использовать так называемую фабрику классов (class factory) — специальный объект, способный создавать экземпляры других объектов. Фабрики классов являются обычными объектами СОМ, доступ к которым осуществляется через интерфейсы. Однако, в отличие от обычных составных объектов, фабрика класса обязательно должна поддерживать интерфейс IC1assFactory. В данном интерфейсе реализовано всего два метода:

CoCreateInstance — создает новый экземпляр класса, объекты которого может создавать данная фабрика. При вызове этого метода клиент должен указать IID, чтобы получить ссылку на нужный ему интерфейс. Клиент не передает этому методу CLSID в качестве параметра, так как класс объекта неявно определяется самой фабрикой;

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

Для получения доступа к фабрике класса предназначен специальный метод библиотеки COM CoGetClassObject. Этой функции передается идентификатор CLSID класса объектов, которые будут создаваться фабрикой (не CLSID самой фабрики!), а также IID интерфейса IClassFactory. Метод возвращает указатель на интерфейс. Затем, с помощью вызова метода CreateInstance интерфейса IClassFactory, создается экземпляр объекта СОМ.

Создание объектов СОМ в Delphi

Понятие объекта, как в терминологии СОМ-модели, так и в системах ООП типа Delphi, имеет практически одинаковый смысл. Интерфейс СОМ больше напоминает объект Delphi, у которого отсутствуют свойства и имеются лишь виртуальные методы. Список функций интерфейса соответствует виртуальной таблице методов Object Pascal. Создать СОМ-иитерфейс можно средствами практически любого языка: достаточно лишь объявить объект с требуемым списком виртуальных методов. Причем задаваемые определения методов должны точно соответствовать определениям функций в самих интерфейсах. Однако из-за необходимости поддержки СОМ-объектом нескольких интерфейсов возникает проблема, связанная с отсутствием в Object Pascal возможности множественного наследования.

Для решения отмеченной проблемы при создании составных объектов в Delphi используется следующий подход. Объект СОМ создается на основе обычного класса Object Pascal, который имеет имя TComObject и является непосредственным потомком TObject. Все свойства и методы объекта СОМ описываются как свойства и методы обычного класса Object Pascal, производного от класса TComObject. При создании объекта СОМ одновременно создается еще один класс, который содержит описание всех интерфейсов объекта СОМ. Данный класс называется составным классом (CoClass) и ему присваивается имя объекта СОМ с приставкой Со (например, при создании объекта СОМ с именем TestObj будет создан CoClass с именем CoTestObj). Таким образом, класс-наследник TComObj содержит код объекта, а составной класс CoClass представляет собой описание экземпляра этого класса в соответствии со спецификацией СОМ.

Объявление и описание составного класса содержится в библиотеке типов, которая всегда создается при создании объекта СОМ.

Для создания объектов СОМ с использованием библиотек типов обычно используется класс TTypedComObject. Данный класс является непосредственным наследником класса TComObj.

Для описания интерфейса в Delphi используется ключевое слово Interface. Описание интерфейса в целом подобно описанию класса. Например, описание интерфейса IDnknown выглядит в Delphi следующим образом:

IUnknown = interface ['{ОООООООО-ОООО-ОООО-СООО-000000000046}'] function QueryInterface(const IID: TGUID: out Obj): integer; stdcall; function _AddRef: integer; stdcall; function _Release: integer; stdcall; end;

Обычно код описания интерфейса генерируется автоматически, на основании данных, задаваемых в редакторе библиотеки типов.

Создание интерфейсов и методов объекта СОМ

Обычно для создания библиотек типов используется специальный язык IDL (Interface Description Language — язык описания интерфейсов). Синтаксис данного языка похож на синтаксис языка C++. Однако в Delphi при создании библиотеки типов обычно используется синтаксис языка Object Pascal.

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

ПРИМЕЧАНИЕ. Код библиотеки типов, разработанной в Delphi на основе языка Object Pascal, может быть, затем экспортирован в формат IDL.