Базовые понятие СОМ-технологии

ЛЕКЦИЯ 1

Введение в современные технологии программирования

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

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

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

Технологическая линия имеет единую регламентированную технологию программирования, все компоненты которой на заданном рабочем интервале согласованы и настроены на конкретные условия конкретного коллектива специалистов. В технологической линии четко указано оборудование, на котором надо работать, и технология его использования – «делай так, а не иначе».

Разработка программного обеспечения уже никогда не будет про­изводиться так, как это делалось раньше. Такова нынешняя реальность: члены одной команды больше не работают вместе. Зачастую они разделены террито­риально, но и находясь в одном офисе, в силу загруженности почти не контактируют между собой или же делают это крайне-неэффективно, тогда как потребность в обмене информацией и согласовании действий очень велика. Нередко даже основным участникам проекта — его руководителям, архитекторам системы, разработчикам программного обеспечения и тестировщикам — бы­вает довольно сложно обменяться необходимыми сведениями, не говоря уже о сотрудниках отдела внедрения и эксплуатации. Проводятся бесконечные совещания, телефонные переговоры, переписка по электронной почте, а работа, тем временем, тормозится, проект не укладывается в бюджет или вообще не со­ответствует требованиям.

1.1.Требования к современным технологиям программирования

Анализ известных и широко распространенных технологий и методов программирования позволяет сформулировать следующие основные требования к ним.

1. Технология программирования должна обеспечить отторжимость программного изделия от его разработчика, т.е. человеческий фактор в программировании должен быть сведен к минимуму. Это необходимо как для разработки программного изделия, так и для грамотного сопровождения, модификации и воспроизводства его на других ЭВМ в других условиях эксплуатации.

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

3. Технология программирования должна быть безбумажной. Это значит, что весь процесс изготовления программного изделия и управления деятельностью коллектива программистов должен быть максимально дебюрократизирован и выполняться за пультом экрана с минимальным расходом бумаги. Документация на программное изделие (техническое описание, инструкции пользователю, инструкции по эксплуатации и т.п.) должны заноситься и храниться в основном на магнитных носителях. Работа пользователя должна обеспечиваться развитой информационно-справочной системой.

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

5. Технология программирования не должна быть связана с языком программирования, так как по современным представлениям он не является определяющим звеном в технологии программирования.

6. Технология программирования должна быть простой в освоении, с автоматически включаемыми средствами подсказки и обучения универсального применения. Эти средства должны быть иерархическими и оперативно привязанными к изготовляемому программному продукту. Должна быть предусмотрена система автоматической генерации средств подсказки и обучения применительно к изготовляемому программному изделию.

7. Технология программирования должна иметь средства автоматической фиксации всех действий, выполняемых в процессе коллективного изготовления программного изделия – должны вестись и храниться в системе журналы (протоколы, дневники разработки). Эти средства должны позволять восстанавливать любые состояния процесса на любом этапе изготовления программного изделия, а также использоваться в процессе его эксплуатации.

1.2. Введение в СОМ, ActiveX и OLE

Любому современному программисту, который желает идти в ногу с последними веяниями, каждые несколько лет приходится переучиваться. Языки (C++, Visual Basic, Java), библиотеки (MFC, ATL, STL), архитектуры (COM, CORBA), кото­рые стали вехами в развитии программирования за последние годы, постепенно уходят в тень лучших или по крайней мере более молодых программных техноло­гий. Вне зависимости от того, нравится это программистам или нет, этот процесс неизбежен. Модель компонентных объектов (Component Object Model, COM) лежит в основе технологии Microsoft ActiveX. Она стала неотъемлемой частью Microsoft Windows, и поэтому изучение ее – обязательная часть дисциплины «Технология программирования». Платформа .NET компании Microsoft — это следующая волна корен­ных изменений, которая идет к нам из Редмонда.

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

Можно сказать, что история программирования - это история попыток написать совершенный код. Разработка как прикладного, так и системного программного обеспечения страдала от бесконечных проволочек, а сами программы отличались чрезвычайной сложностью и непредсказуемым количеством “жучков”. И все же без программ не обойтись, и их надо писать - это ясно. Как написать хорошую программу - вот в чем вопрос!. Для этого нужно обладать способностью, чтобы соединять общие принципы программного проекта с желанием (и даже горячим стремлением) вникнуть в миллиарды мелочей. Это требует не только интеллектуальных колоссальных усилий, но и соответствующего инструментария, который, увы, еще далек от совершенства.

Технология программирования ActiveX и OLE на основе СОМ технологии фирмы Microsoft - еще один шаг к более совершенным, т. е. более надежным и эффективным программам. Но не только более совершенные программы должны делать то, что раньше было невозможно, но и решать новые проблемы. В основе ActiveX и OLE лежит очень простая идея, но, как оказалась, она позволяет существенно повысить эффективность программирования.

Основы технологии ActiveX

Терминология меняется столь же стремительно, как и технология, и даже внутри Microsoft нет единства относительно того, как использовать термины ActiveX и OLE. Считайте, что ActiveX — это нечто, возникшее при столкновении «старого» OLE и Интернета. ActiveX включает в себя не только те возможности Windows, основанные на СОМ, которые мы рассмотрим при изучении дисциплины, но и семейство Microsoft Internet Information Server и программный интерфейс WinInet.

Да, OLE по-прежнему жива и теперь вновь расшифровывается Object Linking and Embedding (связывание и внедрение объектов), как и во время OLE 1.0. Сейчас это просто еще одно подмножество технологии ActiveX, содержащее различные операции, например операцию drag-and-drop (перетащить и отпустить). К сожалению  (или к счастью, если у вас есть ранее написанный код), исходный MFC-код и  Windows API не следуют за последними изменениями терминологии, Поэтому в названиях функций и классов вы увидите множество упоминаний OLE и Оlе, хотя некоторые из этих функций выходят за рамки связывания и внедрения. При использовании кода, сгенерированного MFC Application Wizard, вы можете заметить упоминания о «сервере» (server). Теперь Microsoft резервирует этот термин толь­ко для серверов баз данных и Интернет-серверов. В отношении OLE-серверов применяется новый термин — компонент (component).

Компьютерные секции книжных магазинов забиты книгами по OLE, COM и ActiveX. При изучении дисциплины мы не достигнем той глубины, которой отличаются эти труды, но вы наверняка получите хорошее представление о теории СОМ. Мы уделим боль­шее, внимание связи СОМ с классами библиотеки MFC. Это послужит хорошей подготовкой к практическому использованию СОМ..

COM приносит столько же проблем, сколько решает. Большую часть этой технологии в настоящее время заменяет компонентная модель .NET со своими сборками (assembly) и CLR-средой (common language runtime). Тем не менее СОМ пока в силе, и поэтому первая часть нашего курса будет посвящена изучению СОМ.

Назначение СОМ

СОМ (Component Object Model -модель многокомпонентных объектов) одна из базовых технологий Windows. СОМ— это программная архитектура динамического компоновки ПО. В СОМ предпринята попытка решить проблемы поддержки версий (этим «грешат» DLL) и  сложности механизма удаленного вызова процедур (remote procedure call, RPC).Более того, все новые технологии Windows (Shell, Scripting, поддержка HTML и т.п.) реализуют свои программные интерфейсы (Application Program Interface, API) именно в виде COM-интерфейсов. Таким образом, в настоящее время профессиональное программирование требует понимание модели COM и умения с ней работать. СОМ создавалась с прицелом на решение двух проблем, с которыми повсеместно сталкиваются программисты, работающие в операционной среде Windows.

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

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

Краткая история развития СОМ

Технология СОМ начала развиваться как технология OLE - Object Linking and Embedding (связывание и внедрение объектов). В первую очередь, OLE появилась как одно из направлений развития технологии обмена данными (Dynamic Data Exchange - DDE). Эти технологии позволяли клиентскому приложению получать и сохранять данные от приложения- сервера, а также получать информацию о самом приложении-сервере. Это позволяло при необходимости, по запросу пользователя, запустить его приложение - как правило, после двойного щелчка на пиктограмме, представляющей соответствующие данные.   

Первое воплощение OLE - OLE (Object Linking and Embeding 1 - Связывание и внедрение объектов) - представляло собой механизм создания и работы с составными документами (compound documents). С точки зрения пользователя, составной документ выглядит единым набором информации, но фактически содержит элементы, созданные двумя или несколькими разными приложениями. С помощью OLE 1 пользователь мог, например, объединить электронную таблицу, созданную Microsoft Excel, с текстовым документом “производства” Microsoft Word. Идея состояла в том, чтобы документо-ориентированная (document - centric) модель работы с компьютером позволила бы пользователю больше думать об информации и меньше - о приложениях, ее обрабатывающих. Как следует из определения “ связывание и внедрение”, составные документы можно создать, либо связав два разных документа, либо полностью внедрив один документ в другой.

OLE 1, как и большинство первых версий программных продуктов, была несовершенна. Архитекторам следующей версии предстояло улучшить первоначальный проект. Вскоре они поняли, что составные документы - лишь частный случай более общей проблемы: как разные программные компоненты должны предоставлять друг другу сервисы? Для решения этой проблемы архитекторы OLE создали группу технологий, область применения которых гораздо шире составных документов. Основу OLE 2 составляет важнейшая из этих технологий - Модель многокомпонентных объектов (Component Object Model - COM). Новая версия OLE не только обеспечивает поддержку составных документов лучше, чем первая, но и, несомненно, идет куда дальше простого объединения документов, созданных в разных приложениях. OLE 2 позволяет по-новому взглянуть на взаимодействие любых типов программ.

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

Благодаря этим преимуществам, СОМ скоро стала частью технологий, не имеющих никакого отношения к составным документам. Однако в Microsoft хотели сохранить общее имя для всей группы технологий, в основе которых лежит СОМ. Компания решила сократить название Object Linking and Embeding до OLE - эта комбинация более не рассматривалась как аббревиатура - и опустить номер версии.

По мере совершенствования технологии OLE фирма Microsoft включала в ее состав все новые функциональные возможности, такие как Automation - автоматизация, OLE-элементы управления, за которыми закрепилась аббревиатура OCX.

При новом порядке термин OLE применялся ко всему, созданному на основе парадигмы, представленной СОМ (хотя СОМ использовалась также в продуктах, в названии которых нет слова OLE). OLE более не обозначало только составные документы, но стало маркой любой технологии на основе СОМ. Вообще объединение под одним именем всего программного обеспечения, написанного с применением СОМ, имеет не больше смысла, чем, скажем, объединение всех программ, написанных на С++. СОМ, как и язык программирования типа С++, является универсальным инструментом, который можно использовать для создания программ любых типов. Тем не менее, как по историческим причинам, так и для того, чтобы обозначить явление новой и перспективной технологии, термин OLE использовался в названиях многих (но отнюдь не всех) технологий на основе СОМ. (Название OLE применялось к любой технологии, использующей СОМ).

В начале 1996 года Microsoft ввела в оборот новый термин - ActiveX. Сначала он относился к технологиям, связанным с Интернетом, и приложениям, выросшим из него, вроде WWW (World Wide Web). Поскольку большинство разработок Microsoft в данной области было основано на СОМ, то и ActiveX была непосредственно связана с OLE. Однако очень скоро новый термин стал захватывать территории, традиционно принадлежавшие OLE, и вот теперь все вернулось на круги своя: OLE, как и ранее, обозначает только технологию создания составных документов связыванием и внедрением, а разнообразные технологии на основе СОМ, ранее объединенные под именем OLE, собраны под именем ActiveX. А некоторые технологии, название которых содержало слово “OLE” даже перекрестили: теперь это технологии ActiveX. Новые технологии на основе СОМ, которым раньше полагался ярлык “OLE” - теперь часто получают пометку “ActiveX”.

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

Цели и задачи технологии СОМ

Основная цель технологии СОМ — обеспечение возможности экспорта объектов. Идея экспорта объектов заключается в том, что один модуль создает объект, а дру­гой его использует посредством обращения к методам или сервисам. Конечно, в экспорте простейших объектов обычно не возникает необходимости — любой программист может создать объекты с заданными свойствами в рамках своего приложения — определяющим фактором при этом является время, требующееся для написания кода. Однако предположим, что где-то и кем-то был реализован достаточно сложный алгоритм распознавания текста в файле формата *.bmp, по­лучаемом при сканировании документов. Конечно же, производители сканеров захотят предоставить дополнительные возможности покупателям и пожелают включить такое программное обеспечение в свой пакет. При этом любая фирма будет стараться свести к минимуму число приложений в своем пакете: по воз­можности все сервисы должны предоставляться одним приложением.

На первый взгляд эта проблема решается достаточно просто, по крайней мере, когда в качестве другого модуля используется динамически загружаемая библио­тека (Dynamic Link Library, DLL). В этом случае оба модуля содержатся в одном и том же адресном пространстве. Казалось бы, достаточно создать в DLL объект, а его методы вызвать из основного приложения — и задача решена.

Однако, здесь возникают ряд проблем:

· Программисту, использующему эту библиотеку, потребуется докумен­тация — список методов со списком их формальных параметров. Их реализация «вручную» неизбежно приведет к ошибкам. Для исправления ошибок потребу­ется время, и при этом нет никакой гарантии, что все ошибки будут обнаружены. Хотелось бы, чтобы сам объект мог информировать среду разработки о том, ка­кие методы ему доступны и каковы списки их формальных параметров;

· Сложности работы с различными модулями этим не ограничиваются. Напри­мер, если в одном из модулей зарезервирована память для хранения данных, то в другом модуле без специальных мер нельзя ни освободить ее, ни изменить ее размер. Это связано с тем, что в каждом модуле имеется собственный менеджер памяти. Если один модуль выделяет память, то в менеджере памяти другого мо­дуля это никак не фиксируется. Для реализации операций, связанных с примене­нием в различных модулях общих областей памяти, необходимо наличие общего менеджера памяти. Для его создания в Delphi используется модуль ShareMem;

· Еще одна проблема возникает при обращении к объекту, созданному другим приложением. В этом случае указатель на объект в памяти, созданный в одном приложении, является недействительным для другого приложения. Если пере­дать указатель из одного приложения в другое (это можно сделать, скопировав его в буфер обмена или используя метод PostMessage), другое приложение будет обращаться совсем не к тем ячейкам оперативной памяти компьютера, в которых реально находятся данные. В лучшем случае сразу же произойдет исключение, в худшем — при попытке записи данных — будет разрушено ядро Windows. Эту проблему можно представить более глобально, если рассматривать возможность создания объекта на одном из компьютеров, а использования его через сеть на другом.

· Очередная проблема возникает при передаче двоичных данных от одного при­ложения к другому. Многие языки программирования имеют разное внутреннее представление переменных (например, строки, логические значения), и при по­лучении данных от заранее неизвестного приложения их интерпретация подчас невозможна.

· Можно выделить и другие проблемы, которые встречаются в традиционном программировании, например:

Ø поиск установленной копии приложения, реализующего требуемые сервисы, и корректная его инициализация;

Ø обеспечение корректной работы приложения-сервера одновременно с не­сколькими клиентами;

Ø управление памятью, выгрузка из памяти приложения-сервера, когда необ­ходимость в нем отпадет и, наоборот, предотвращение несвоевременной вы­грузки.

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

Дальнейшее развитие СОМ

Хотя на сегодняшний день СОМ представляет собой законченную технологию, ее развитие на этом этапе не прекратилось. Следующее поколение этой технологии - СОМ+. В новой версии реализованы два наиболее существенных усовершенствования:

· автоматическая обработка подсчета ссылок;

· СОМ - объекты можно будет инспектировать во время выполнения.

Технологии в современном мире развиваются непостижимо быстро. Сначала ни у кого не было настольных компьютеров, в 80-х почти все обзавелись машинами под управлением MS-DOS, и, наконец, к середине 90-х компьютеры под управлением Microsoft Windows заполонили мир. И, по-видимому, технологии сделают один виток. В конце 90-х Web-сайты разрабатывались вручную, с использованием «простого» языка HTML (Hypertext Markup Language), CGI (Common Gate Interface), DLL-библиотек ISAPI (Internet Server Application Programming Interface), Java и ASP-страниц (Active Server Pages). В июле 2000 г. Microsoft возвестила о намерении заменить все это технологией .NET.

Сейчас Microsoft вовсю работает над .NET. Пару лет назад для создания сайта нужно было лишь установить сервер, приобрести IP-адрес и «выложить» на сайт какую-то информацию. После этого сайт становился доступен всем — достаточно было знать URL-адрес. Коммерческие предприятия использовали Web для размещения данных, которые могли пригодиться клиентам. Web-среда также оказалась ценным исследовательским инструментом и средством распространения информации.

В ИТ-мире ближайшего будущего Web будет играть первую скрипку. Однако  ситуация изменится: до этого содержимое Web-сайтов предназначалось пользователям, теперь же с этой информацией будут работать другие компьютеры. То есть доступ к содержимому Web-сайтов станет возможен из программ — благодаря Web-сервисам. Согласно концепциям .NET ответственность за организацию многофункционального пользовательского интерфейса возлагается на сервер.

В условиях эйфории относительно Web-сервисов и пользовательских интерфейсов, поддерживаемых сервером, может показаться, что автономные приложения и клиентские сценарии — прерогатива таких средств, как библиотека MFC. (Microsoft Foundation Class Library), — окажутся за бортом истории. Однако вряд ли исчезнет потребность в полнофункциональном пользовательском интерфей­се. Многие полагали, что «персоналки» и распределенные технологии естествен­ным путем вытеснят мейнфреймы и миникомпьютеры, а оказалось, что ПК и рас­пределенные вычисления лишь дополнили общую картину ИТ-мира. Принципы Web-сервисов в .NET и поддерживаемые сервером многофункциональные пользо­вательские интерфейсы стали еще одним вариантом, доступным разработчикам. Полнофункциональные пользовательские интерфейсы никуда не уйдут из боль­шинства приложений, которые прекрасно уживутся с другими приложениями с другого типа интерфейсами (в том числе поддерживаемыми сервером Web-интер­фейсы).

MFC — зрелый, хорошо изученный инструмент, обеспеченный обширной под­держкой сторонних компаний. Какое-то время эта библиотека останется одним из самых производительных способов создания полнофункциональных автоном­ных приложений.

А что будет с СОМ? Эта технология позволила решить массу проблем органи­зации распределенных вычислений, но у нее есть ряд серьезных недостатков, как правило, связанных с поддержкой версий и информации о типах. Работа .NET ос­нована на общеязыковой среде исполнения, или CLR (Common Language Runtime). Она берет на себя функции СОМ по обеспечению совместимости. И .NET и CLR-среда будут рассмотрены во второй части курса.

СОМ и CLR-среда основаны на различных компонентных архитектурах, и все же Microsoft позаботилась о механизмах «мирного сосуществования» этих техно­логий. Обычно удается без проблем обеспечить совместимость СОМ и CLR. Ма­ловероятно, что в мире .NET вы станете использовать СОМ в качестве компонен­тной архитектуры, однако вряд ли откажетесь от ATL (Active Template Library) Server — высокопроизводительного инструмента создания Web-сайтов.

Базовые понятие СОМ-технологии

Все технологии OLE и ActiveX, которые будут рассматриваться, построены на основании, обеспеченном СОМ. Итак, что же такое СОМ? Чтобы ответить на этот вопрос, зададимся сначала другим: “Каким образом одна часть программного обеспечения должна получать доступ к сервисам, предоставляемым другой частью?” На сегодняшний день ответ зависит от того, что представляют эти части. Рассмотрим следующий рисунок (рис.1.1). Приложения, например, скомпонованные с библиотекой, могут пользоваться ее сервисами, вызывая функции из этой библиотеки. Приложение также может использовать сервисы другого приложения, являющегося совершенно отдельным процессом. В этом случае два таких локальных процесса взаимодействуют посредством некого механизма связи, который обычно требует определения протокола между этим приложениями (набор сообщений, позволяющих одному приложению выдавать запросы, а другому соответствующим образом отвечать на них). Еще пример - приложение, использующее сервисы операционной системы. Здесь приложение обычно выполняет системные вызовы, обрабатываемые операционной системой. Наконец, приложению. могут понадобиться сервисы, предоставляемые программным обеспечением, выполняемым на другой машине, доступ к которой осуществляется по сети. Получать доступ к таким сервисам можно многими способами, такими как, обмен сообщениями с удаленным приложением или вызовы удаленных процедур.

В принципе проблема одна: одна часть программного обеспечения должна получить доступ к сервису, предоставляемому другой частью. Но в каждом отдельном случае механизм доступа разный: вызовы локальных функций, передача сообщений средствами связи между процессорами, системные вызовы (которые с точки зрения программиста выглядят практически так же, как и вызовы функций) или какая-то разновидность сетевых коммуникаций. Зачем все это? Не проще ли определить один общий способ доступа ко всем видам программных сервисов независимо от способа их реализации?

 

 


Рис.1.1. В отсутствии СОМ для доступа к сервисам, предоставляемых библиотеками, локальными процессами, операционной системой или удаленными процессами применяются разные механизмы.

Этим и занимается СОМ. Она предоставляет стандартный механизм, с помощью которого одна часть программного обеспечения предоставляет свои сервисы другой и который работает во всех описанных выше случаях. Смысл ее заключается в том, чтобы применить объектный подход не на уровне одного приложения и классов, а на уровне нескольких приложений, в том числе приложений, расположенных на удаленном компьютере. Общая архитектура сервисов в библиотеках, приложениях, системном и сетевом программном обеспечении позволяет СОМ (Component Object Model - Модель многокомпонентных объектов) изменить подход к созданию программ.

Функционирование СОМ

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

Представьте себе, например, корректор орфографии, реализованный в виде объекта СОМ. Такой объект может поддерживать интерфейс, включающий методы типа LookUpWord (НайтиСлово), AddToDictionary(ДобавитьКСловарю) и RemoveFromDictionary (УдалитьИзСловаря). Если позднее разработчик объекта СОМ захочет добавить к этому объекту поддержку словаря синонимов, то объекту потребуется еще один интерфейс, возможно, с единственным методом, вроде ReturnSynonyrn (НайтиСиноним). Методы каждого из интерфейсов сообща предоставляют связанные друг с другом сервисы: либо корректировку правописания, либо доступ к словарю синонимов.

Или вообразите объект СОМ, представляющий Ваш счет в банке. К одному из его интерфейсов Вы можете иметь непосредственный доступ, и он содержит методы Deposit (ПоложитьНаСчет), Withdraw (СиятьСоСчета) и CheckBalance (ПроверитьОстаток). Тот же объект может поддерживать и другой интерфейс, содержащий методы вроде ChangeAccountNumber (ИзмснитьНомерСчета) и CloseAccount. (ЗакрытьСчет), которые могут вызываться только сотрудниками банка. И здесь каждый интерфейс содержит методы, связанные друг с другом.

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

 

 


Рис 1-2 Доступ к сервисам объекта СОМ осуществляется через его интерфейсы

На рис.1.3 один из интерфейсов объекта СОМ показан крупным планом. Он обеспечивает доступ к сервису коррекции орфографии и содержит три метода, упомянутые выше. Если другой интерфейс объекта обеспечивает сервис тезауруса, то на рисунке был бы изображен только метод ReturnSynonym.(Приведенная схема несколько упрощена — на самом деле все интерфейсы содержат несколько стандартных методов, которые здесь не показаны.)

 

 


                                                                                        

 

Рис.1.3. Каждый интерфейс содержит один или несколько методов

Чтобы вызывать методы интерфейса объекта СОМ, клиент должен получить указатель на этот интерфейс. Обычно СОМ-объект предоставляет свои сервисы посредством нескольких интерфейсов, и клиенту требуется отдельный указатель для каждого интерфейса, методы которого он намерен вызывать. Например, клиенту нашего простого объекта СОМ понадобился бы один указатель интерфейса для вызова методов интерфейса корректора орфографии и другой — для вызова методов интерфейса словаря синонимов. Клиент с указателями на два интерфейса одного и того же СОМ-объекта показан на рис. 1.4.

 

 


Рис. 1.4. Клиент с указателями на два интерфейса объекта СОМ

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

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

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

 

 

 


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

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

Объект всегда работает в составе сервера СОМ. Сервер может быть динами­ческой библиотекой или исполняемым файлом. Объект может иметь собст­венные свойства и методы или использовать данные и службы сервера.

Для доступа к методам объекта клиент должен получить указатель на соот­ветствующий интерфейс. Для каждого интерфейса существует собственный указатель. После этого клиент может использовать сервисы объекта, просто вызывая его методы. Доступ к свойствам объектов осуществляется только через его методы.

Предположим, что объектСОМ встроен в электронную таблицу и обеспечи­вает доступ к математическим операциям. Будет логично разделить математи­ческие функции на группы по типам и создать для каждой группы собствен­ный интерфейс. Например, можно выделить линейные, тригонометрические, агрегатные функции и т. д. На рис. 1.6 объект расположен внутри сервера - электронной таблицы. Интерфейсы обозначены маленькими кружками, связанными с объектом. Пусть интерфейс линейных функций называется ILinear, а интерфейс агрегатных функций - IAggregate.

Примечание

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

 

 

 


Рис. 1.6. Сервер, объект и его интерфейсы

 

Рис 1.6. Структура сервера

На рис.1.7 представлена схема взаимодействия клиента с объектом COM.

Чтобы получить доступ к агрегатной функции расчета среднего, клиент должен получить указатель на интерфейс IAggregate, а затем обратиться к этой функции.

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

 

 


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

Возникает закономерный вопрос — как проходит создание и инициализация объекта СОМ при первом обращении клиента? Сомнительно, чтобы опера­ционная система самостоятельно создавала экземпляры всех зарегистриро­ванных в ней классов в надежде, что один из них понадобится. А ведь для работы объекта требуются еще и серверы. Представьте, что каждый раз при загрузке Windows настойчиво запускает Word, Excel, Internet Explorer и т. д.

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

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

После создания наступает очередь инициализации — объект должен загру­зить необходимые данные, считать настройки из системного реестра и т. д. За это отвечают специальные объекты СОМ, которые называются моникерами (monikers). Они работают скрытно от клиента. Обычно моникер создает­ся вместе с классом.

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

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

Эта информация содержится в библиотеке типов, которая создается при по­мощи специального языка описания интерфейса (Interface Definition Language,IDL).

Объект

Центральным элементом СОМ является объект. Приложения, поддержи­вающие СОМ, имеют в своем составе один или несколько объектов СОМ. Каждый объект представляет собой экземпляр соответствующего класса и содержит один или несколько интерфейсов. Что же такое объект СОМ?

Не вдаваясь пока в подробности реализации объектов СОМ в Delphi, можно сказать, что объект СОМ несколько отличается от обычного объекта.

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

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

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

Примечание

Некоторые языки программирования, например, Java, позволяют объекту иметь несколько интерфейсов.

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

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

Таким образом, объект СОМ с точки зрения ООП несомненно является объектом. Однако, как ключевой элемент технологии СОМ, он обладает ря­дом особенностей реализации базовых механизмов.

Интерфейс

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

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

Рассмотрим небольшой пример. На улице случайно встретились два челове­ка: местный житель (объект СОМ) и заблудившийся иностранец (клиент). Предусмотрительный иностранец захватил с собой словарь (библиотека ти­пов или интерфейс IUnknown). Иностранцу нужны знания местного жителя о городе. Он достал ручку и бумагу и, заглянув в словарь, составил фразу и старательно перерисовал незнакомые слова на бумагу. Местный житель ока­зался не промах. Он прочитал фразу, отобрал у иностранца словарь, соста­вил по нему собственную фразу и тоже написал ее на бумаге. И все закон­чилось хорошо: довольный клиент (иностранец) получил от объекта СОМ (местного жителя) результат работы службы (информацию о дороге), а ме­стный житель ушел вместе со словарем.

Как вы уже догадались, в этом примере интерфейсом является бумага и руч­ка: иностранец не знает чужого языка, зато знает, как правильно спросить, чтобы ему ответили.

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

Интерфейс является контрактом между программистом и компилятором:

§ программист обязуется реализовать все методы, описанные в интерфейсе, и следовать требованиям на реализацию некоторых из них;

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

Таким образом, СОМ является языково-независимой технологией и может использоваться в качестве «клея», соединяющего программы, написанные на раз­ных языках.

Объявление интерфейса включает в себя описание методов и их параметров, но не включает реализации методов. Кроме этого, в объявлении может указы­ваться идентификатор GUID интерфейса — уникальное 16-байтовое число, сге­нерированное по специальным правилам, гарантирующим его статистическую уникальность; о GUID мы расскажем чуть позже.

Интерфейсы могут наследоваться. При наследовании подразумевается, что унаследованный интерфейс должен включать в себя все методы предка.

Таким образом, необходимо понять следующее.

Интерфейс — это не класс. Класс может выступать в роли реализации интер­фейса, но класс содержит код методов на конкретном языке программирова­ния, а интерфейс — нет.

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

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

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

Для идентификации каждый интерфейс имеет два атрибута. Во-первых, это его имя, составленное из символов в соответствии с правилами используе­мого языка программирования. Каждое имя должно начинаться с символа "I". Это имя используется в программном коде. Во-вторых, это глобальный уникальный идентификатор (Globally Unique IDentifier, GUID), который представляет собой гарантированно уникальное сочетание символов, прак­тически не повторяемое ни на одном компьютере в мире. Для интерфейсов такой идентификатор носит название IID (Interface Identifier).

В общем случае клиент может не знать, какие интерфейсы имеются у объек­та. Для получения их перечня используется базовый интерфейс IUnknown (см. ниже), который есть у любого объекта СОМ.

Затем клиенту необходимо знать, какие методы имеет выбранный им интерфейс. Для этого разработчик должен распространять описание методов интерфейсов вместе с объектом. Эту задачу помогает решать язык IDL (он также используется в библиотеках типов). Его синтаксис очень похож на C++.

Теперь осталось сделать самое важное — правильно вызвать сам метод. Для этого в СОМ описана реализация интерфейса на основе стандартного двоич­ного формата. Это обеспечивает независимость от языка программирования.

 

Рис.1.8. Формат интерфейса СОМ

Доступный клиенту указатель интерфейса ссылается на внутренний указатель объекта и, через него, на специальную виртуальную таблицу (рис. 1.8). Эта таблица содержит указатели на все методы интерфейса. (Не правда ли, очень похоже на таблицу виртуальных методов объекта в ООП.)

Примечание

Первые три строки таблицы интерфейса всегда заняты под методы интерфей­са IUnknown, так как любой интерфейс СОМ является наследником этого интерфейса.

В результате, вызов метода клиентом проходит по цепочке указателей и по­лучает указатель на конкретный метод, а затем исполняется соответствую­щий программный код.

Интерфейс lUnknown

Каждый объект СОМ обязательно имеет интерфейс IUnknown. Этот интер­фейс имеет всего три метода, но они играют ключевую роль в функциони­ровании объекта.

Метод QueryInterface возвращает указатель на интерфейс объекта, иденти­фикаторIID которого передается в параметре метода. Если такого интер­фейса объект не имеет, метод возвращает Null.

Обычно при первом обращении к объекту клиент получает указатель на ин­терфейс. Так как любой интерфейс является потомком IUnknown, то любой интерфейс имеет и метод QueryInterface. Поэтому в общем случае не важно, какой именно интерфейс может использовать клиент. При помощи метода QueryInterface он может получить доступ к любому интерфейсу объекта.

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

Поэтому при передаче наружу очередного указателя на интерфейс, объект увеличивает специальный счетчик ссылок на единицу. Если один клиент передает другому указатель на интерфейс этого объекта, то клиент, полу­чающий указатель, обязан еще раз увеличить (инкрементировать) счетчик ссылок. Для этого используется метод AddRef интерфейса IUnknown.

При завершении работы с интерфейсом клиент обязан вызвать метод Release интерфейса IUnknown. Этот метод уменьшает счетчик ссылок на единицу. По­сле обнуления счетчика объект уничтожает себя.

Сервер

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

· Внутренний сервер (in-process server) реализуется динамическими библио­теками, которые подключаются к приложению-клиента и работают в од­ном с ним адресном пространстве.

· Локальный сервер (local server) создается отдельным процессом, который работает на одном компьютере с клиентом.

· Удаленный сервер (remote server) создается процессом, который работает на другом компьютере по отношению к клиенту.

Рассмотрим локальный сервер. Получаемый клиентом указатель интерфейса в этом случае ссылается на специальный proxy-объект СОМ (назовем его заместителем), который функционирует внутри клиентского процесса. За­меститель предоставляет клиенту те же интерфейсы, что и вызываемый объ­ект СОМ на локальном сервере. Получив вызов от клиента, заместитель упаковывает его параметры, и при помощи служб операционной системы передает вызов в процесс сервера. В локальном сервере вызов передается еще одному специализированному объекту — заглушке (stub), который рас­паковывает вызов и передает его требуемому объекту СОМ. Результат вызо­ва возвращается клиенту в обратном порядке.

Рассмотрим удаленный сервер. Он функционирует так же, как и локальный сервер. Однако передача вызовов между двумя компьютерами осуществляет­ся средствами DCOM — с помощью механизма вызова удаленных процедур (Remote Procedure Call, RPC) (рис.1.9).

 

 


Рис.1.9 Организация маршалинга и демаршалинга

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

В описанных выше реализациях серверов за выполнение этих опера­ции отвечают заместитель и заглушка. Эти типы объектов создаются совме­стно с основным объектом СОМ. Для этого применяется IDL.

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

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

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

· идентификатор класса (Class Identifier, CLSID), который однозначно оп­ределяет класс объекта;

· тип сервера объекта — внутренний, локальный или удаленный;

· для локальных и внутренних серверов сохраняется полное имя динамиче­ской библиотеки или исполняемого файла;

· для удаленных серверов записывается полный сетевой адрес.

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

Библиотека при помощи диспетчера управления службами (Service Control Manager, SCM) обращается к системному реестру, по идентификатору клас­са находит информацию о сервере и запускает его. Сервер создает экземп­ляр класса — объект и возвращает библиотеке указатель на запрошенный интерфейс.

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

Для неявной инициализации созданного объекта (установки значений свойств) может использоваться специальный объект — моникер. Также кли­ент может инициализировать объект самостоятельно, применив специаль­ные интерфейсы (IPersistFile, IPersistStorage,IPersistStream).

Фабрика класса

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

Объект СОМ имеет право называться фабрикой класса, если он поддержи­вает интерфейс IClassFactory. В нем реализованы всего два метода:

· CoCreateInstance — создает новый экземпляр класса.Все необходимые параметры, кроме iid, метод получает от фабрики класса. В этом его отличие от одноименной общей функции библиотеки;

· LockServer — оставляет сервер функционировать после создания объекта.

Примечание

На самом деле общий метод CoCreateInstance при помощи переданного ему clsid осуществляет вызов соответствующей фабрики класса и метода CoCreateInstance интерфейса IClassFactory.

Для вызова фабрики класса существует специальная функция СoGetClassObject. В качестве параметра ей передается clsid нужного клас­са и iid интерфейса (IClassFactory). Функция ищет требуемую фабрику и возвращает указатель на интерфейс. С его помощью, используя метод CoCreatelnstance, клиент заставляет фабрику класса создать объект.

 

 

 


Рис. 1.10. Создание первого экземпляра объекта с помощью библиотеки СОМ и системного реестра

Библиотека типов

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

Дня создания библиотеки типов, описанной при помощи операторов IDL, используются специальные компиляторы. Доступ к библиотеке осуществля­ется по clsid класса объекта. Кроме того, библиотека имеет собственный GUID, который сохраняется в системном реестре при регистрации объекта.

Каждая библиотека типов имеет интерфейс ITypeLib, который дает возмож­ность работать с ней, как с единым объектом. Для доступа к информации об отдельном интерфейсе используется интерфейс ITypeInfo.

Для доступа к библиотеке по GUID используется функция LoadRegTypeLib. Если клиенту известно имя файла библиотеки, то можно воспользоваться функцией LoadTypeLib.

Объекты СОМ в Delphi

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

Реализовать такое требование напрямую в рамках стандартных подходов ООП довольно затруднительно. И разработчики Delphi нашли следующий выход.

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

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

Итак, стандартное объявление класса на Object Pascal обеспечивает создание кода объекта — именно оно компилируется в двоичный код. CoClass явля­ется надстройкой или оболочкой этого кода, он обеспечивает представление экземпляра объекта в соответствии со спецификацией СОМ и гарантирует, что обращение клиента к объекту будет обработано корректно.

Класс TComObject в совокупности с создаваемым для каждого объекта эк­земпляром CoClass обладает всеми рассмотренными выше признаками объ­екта СОМ. Он может поддерживать произвольное число интерфейсов и в том числе базовый интерфейс IUnknown.

Для обеспечения работы объекта СОМ с библиотекой типов от базового класса TComObject порожден новый класс TTypedComObject. Дополнительно он имеет еще один интерфейс — IProvideClassInfo. Если этот интерфейс имеет доступ к библиотеке типов, то для получения полной информации об объекте достаточно знать его идентификатор класса. Этот класс использует­ся для создания объектов с использованием библиотеки типов.

GUID Globally Unique Identifier — глобально уникаль­ный идентификатор

Теперь перейдем к рассмотрению проблемы выбора необходимого интерфейса из того многообразия, которое представлено в иерархии интерфейсов. Вот тут имеется существенное расхождение между идентификацией класса и интерфей­са. Эти различия связаны с тем, что классы используются внутри одного и того же модуля, а интерфейсы — в различных модулях. Для того чтобы создать класс с заданными свойствами, его имя просто указывается перед конструктором. Про­граммист сам следит за тем, чтобы имена различающихся классов не совпадали, а при их совпадении — чтобы вызывался конструктор нужного класса. При рабо­те с несколькими модулями такое невозможно, поскольку модули могут созда­ваться разными разработчиками в разное время.

Если бы интерфейсы различались только по именам, то при случайном совпа­дении имен (а это происходило бы довольно часто: имя обычно несет в себе смысло­вую нагрузку) двух интерфейсов, реализованных в разных модулях, вместо одного модуля загружался бы другой. Поэтому для идентификации интерфейса исполь­зуется структура типа GUID (Globally Unique Identifier — глобально уникаль­ный идентификатор), которая имеет размер 16 байт (128 бит). Единственный тип данных, который предопределен для интерфейса, — это GUID. Каждый СОМ-интерфейс содержит собственный уникальный идентификатор GUID. Если разра­ботчик реализует новый интерфейс, то этот интерфейс обязан иметь GUID, при­чем уникальность должна соблюдаться не только в рамках данного компьютера разработчика, но и для всего мира. Эта глобальная уникальность достигается путем генерации GUID как псевдослучайного числа по алгоритму, определенному кон­сорциумом Open Systems Foundation (сейчас он носит название Open Group) и ис­пользующему некоторые специфические характеристики данного компьютера (в частности, IP-адрес сетевого адаптера). Алгоритм учитывает текущую дату и время, номер текущего программного процесса для приложения, создающего GUID, и уникальный идентификатор, хранящийся в недрах сетевой карты, если таковая уста­новлена в компьютере, а также некоторые другие данные. К счастью, нам нет необхо­димости углубляться во все перипетии формирования GUID. Возникает естественный вопрос — можно ли в двух разных местах случайно сгенеририровать два одинаковых иден­тификатора GUID? Ответ на этот вопрос заключается в возможном диапазоне значений GUID. Он настолько огромен, что если генерировать GUID со скоростью 1000 000 идентификаторов в секунду, то за все время существования Вселенной с вероятностью 95% не будут созданы два одинаковых идентификатора GUID.

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

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

Жизненно важно, чтобы при создании нового интерфейса формировался новый, нигде ранее не встречавшийся GUID (т.е. ни на одном компьютере мира, где этот ин­терфейс может гипотетически использоваться). Ни в коем случае нельзя копировать значение GUID из одного интерфейса в другой.

Все заботы о формировании GUID в нашем случае берет на себя среда программирования (Delphi, Visual C++). Например, в Delphi за­прос на формирование посылается в том случае, если вы расположите курсор в том месте программного кода, куда должен быть вставлен GUID, и нажмете <Ctrl+Shift+G>. При этом Delphi сформирует запрос к Windows-API функции CoCreateGuid, а послед­няя уже и сформирует уникальный идентификатор.

Создание приложения в Delphi, демонстрирующего формирование GUID.

1.Запустите Delphi.

2. На форму поместите компоненты TMemo и TButton, разместив их примерно так как, как указано на рис. 1.11.

3. Свойству Name и Caption присвойте значение GenerateGuid и свяжите код процедуры TForm1.btnGenerateClickf(Sender; TObject) из программы на листинге 1.1.

В листинге 1.1. приведен текст программы, которая непосредственно вызывает CoCreateGuid и формирует в результате список уникальных идентификаторов.

 

 

Рис. 1.11. GUIDDemo демонстрирует, как вызвать из приложения функцию CoCreateGuid.

Листинг 1.1. МодульMainForm приложенияGUIDDemo

unit MainForm;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics,     Controls, Forms, Dialogs, StdCtrls, ComObj, ActiveX;

type TForm1 = class(TForm)

btnGenerate: TButton;

Memol: ТМешо;

procedure btnGenerateClick(Sender: TObject);

private

{ Объявление закрытых членов}

 public

{ Объявление общедоступных членов }

end;

var Form1: Tform1;

implementation

{$R *.DFM}

procedure TForm1.btnGenerateClick(Sender; TObject);

var

Guid: TGUID;

begin

CoCreateGuid(Guid);

 Memol.Lines.Add(GuidToString(Guid));

end;

end.

На рис. 1.11 показано, что выводит на экран приложение GUIDDemo после запуска на выполнение.

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

Структура TGUID определена в Delphi следующим образом:

TGUID = record

Dl: LongWord;

D2: Word;

D3: Word;

D4: array[0..7] of Byte;

end;

Четыре поля в TGUID соответствуют четырем составным элементам GUID. Уни­кальные идентификаторы в Delphi могут быть представлены двумя способами. На­пример, если объявить некоторый интерфейс IformattedNumber следующим образом:

IFormattedNumber = interface

['{2DE825C1-EADF-11D2-B39F-0040F67455FE}']

  function FormattedString: string;

end;

 то следующие два объявления приведут к одному и тому же результату:

MyGuid := ['{2DE825C1-EADP-11D2-B39F-0040F67455FE}']

MyGuid *TGUID =[Dl: $2DE825C1; D2: $EADF; D3: $11D2; D4: ($B3, $9F, $00, $40, $F6, $74,$55,$FE}]

 Естественно, вариант, представленный в первой строке, читается гораздо легче. Объявление интерфейса — это только первый шаг. После того как интерфейс объ­явлен, нужно позаботиться о его реализации. Как это делается, мы рассмотрим в сле­дующем разделе.

Автоматическое управление памятью и подсчет ссылок

Кроме предоставления независимого от языка программирования доступа к ме­тодам объектов, СОМ реализует автоматическое управление памятью для СОМ-объектов. Оно основано на идее подсчета ссылок на объект. Любой клиент, кото­рому требуется использовать СОМ-объект после его создания, должен вызвать заранее предопределенный метод, который увеличивает внутренний счетчик ссы­лок на объект на единицу. По завершении использования объекта клиент вызы­вает другой его метод, уменьшающий значение того же счетчика. При достиже­нии счетчиком ссылок нулевого значения СОМ-объект автоматически удаляет себя из памяти. Такая модель позволяет клиентам не вдаваться в подробности реализации объекта, а объекту — обслуживать несколько клиентов и корректно очистить память по завершении работы с последним из них.

Объявление интерфейсов

Для поддержки интерфейсов Delphi расширяет синтаксис языка Pascal допол­нительными ключевыми словами. Объявление интерфейса в Delphi реализуется с помощью ключевого слова interface:

type

IMyInterface =interface

 [‘{412AFFOO-5C21-11D4-84DD-C8393F763A13}']

procedure DoSomething(var I: Integer);stdcall;

function DoSomethingAnother(S:String): Boolean;

end;

IMyInterface2 = interface (IMyInterface)

[‘{412AFF01-5C21-11D4-84DD-C8393F763A13}’]

procedure DoAdditional(var I: Integer): stdcall;

end:

Для генерации нового значения GUID в среде разработки Delphi использует­ся сочетание клавиш Ctrl+Shift+G.

Интерфейс lUnknown

Базовым интерфейсом в модели СОМ является интерфейс IUnknown. Любой дру­гой интерфейс наследуется от IUnknown и должен реализовать объявленные в нем методы. Интерфейс IlUnknown объявлен в модуле System.pas следующим образом:

Type

lUnknown -interface

['{00000000-0000-0000-С000-000000000046}‘]

function QueryInterface(const IID: TGUID;out Obj ): HResult;stdcall:

function _AddRef: Integer: stdcall:

function _Re1ease: Integer: stdcall;

end;

На уровне интерфейса IUnknown, который является предком для всех интер­фейсов, уже реализовано корректное решение проблемы резервирования и осво­бождения ресурсов операционной системы. Напомним эту проблему: если в ка­ком-нибудь модуле были затребованы ресурсы операционной системы, то их нельзя возвратить системе в другом модуле.

Для начала вспомним, каким образом происходит резервирование и возвра­щение системных ресурсов при работе с классами. При создании экземпляра клас­са вызывается его конструктор, который получает у системы все необходимые ему ресурсы. После того как рабочая копия становится ненужной, вызывается деструктор объекта. При этом если все реализовано корректно, ресурсы вновь возвращаются системе.

Методы интерфейса, как будет показано ниже, также реализуются в классах. Однако описанную выше методику применить в этом случае невозможно. Если в интерфейсе будет определен метод Destroy, который непосредственно обращается к деструктору, то при попытке вызвать его из модуля, где была получена ссылка на интерфейс, произойдет исключение. Это связано с тем, что различные модули, даже реализованные на одном языке программирования, имеют собственные ме­неджеры памяти, и освобождение ресурсов должно происходить в рамках того модуля, в котором они были востребованы.

И еще следует принять во внимание, что один и тот же интерфейс может быть затребован одновременно несколькими клиентами. Например, пусть два клиент­ских приложения одновременно работают с Microsoft Word. Если производить освобождение ресурсов интерфейса тем же способом, что и освобождение ресур­сов класса, то первое клиентское приложение просто закроет Word. Второе, в об­щем случае, не будет уведомлено об этом, поэтому оно сохранит ссылку на несу­ществующий объект. Нетрудно догадаться, что произойдет в этом случае при попытке вызвать методы этого объекта.

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

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

Вызов деструктора происходит из сервера — поэтому ресурсы освобождаются корректно. В вышеприведенном примере счетчик ссылок становится равным нулю при вызова метода Release обоими клиентами.

Интерфейс IUnknown содержит также метод QueryInterface, который используется для получения клиентом ссылок на интерфейс. Клиент вызывает QueryInterface интерфейса IUnknown сервера и указывает идентификатор (Interface Identifier — идентификатор интерфейса) того интерфейса, ссылку на который он хочет получить (IID — это тот же самый тип данных, что и GUID, и применяется он для идентификации интерфейсов). Как получить ссылку на интерфейс IUnknown сервера, будет рассмотрено позднее при описании интерфейса IC1assFactory. Метод QueryInterface обязан проверять все идентификаторы IID, интерфейсов, реализованные в данном классе многокомпонентного объекта (CoClass, СОМ-класс). Если будет найден совпадающий идентификатор IID, то метод QueryInterface обязан сделать следующее.

1. Вызвать конструктор, если не был создан экземпляр класса, в котором реализован интерфейс.

2. Вызвать метод AddRef для затребованного интерфейса и тем самым увеличить счетчик ссылок на 1. Иногда для группы интерфейсов реализуется общий счетчик ссылок.

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

4. Возвратить результат S_OK.

Если же интерфейс с данным идентификатором IID не поддерживается, то в нетипизированную переменную Р возвращается nil, и результат вызова должен быть равен E_NOINTERFACE. Метод Querylnterface может быть реализован, например следующим образом:

function TMyClassFactory.QueryInterface(const iid: TIID; var Р): HResult;

begin

if IsEqualIID(iid, IID_ICIassFactory) or

IsEqualIID(iid, IID_IUnknown) then

begin

   Pointer(P) := Self:

   AddRef:

   Result := S_OK;

end

else

begin

Pointer(P) := nil;

Result := E_NOINTERFACE;

end:

end:

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

Контрольные вопросы

1. Что такое компонент? Чем он отличается от класса?

2. Что такое интерфейс?

3. Какие формы представления интерфейса вы знаете?

4. Чем полезен интерфейс?

5. Каково назначение COM? Какие преимущества дает использование COM?

6. Чем COM-объект отличается от обычного объекта?

7. Что должен иметь клиент для использования методов COM-объекта?

8. Как идентифицируется COM-интерфейс?

9.  Как описывается COM-интерфейс?

10. Как реализуется COM-интерфейс?

11. Чего нельзя делать с COM-интерфейсом? Обоснуйте ответ.

12. Объясните назначение и применение метода QueryInterface?

13. Объясните назначение и применение методов AddRef и Release?

14. Что такое сервер COM-объекта и какие типы серверов вы знаете?

15. В чем назначение библиотеки COM?

16. Как создается одиночный COM-объект?

17. Как создается несколько COM-объектов одного и того же класса?

18. Как обеспечить использование нового COM-класса старыми клиентами?

19. В чем состоит особенности повторного использования COM-объекта?

20. Какие требования предъявляет агрегация к внутреннему COM-объекту?

21. Что такое маршалинг и демаршалинг?

22. Поясните назначение посредников и заглушки.

23. Зачем нужна библиотека типа и как она описывается?

Литература

1. Вельбицкий И.В. Технология программирования. –К.: Техніка, 1984. – 279 с., ил. – (Б-ка инженера). – Библиогр.: с. 274 – 277.

2. Давид Чеппел. Технология ActiveX и OLE/Пер. с англ.. – М: Издательский отдел „Русская Редакция” ТОО „Channel Trading Ltd.@, 1997. – 320  с.: ил.

3. Орлов С.А. Технология разработки программного обеспечения: Учебник. – СПб.: Питер, 2002. – 464 с.: ил.

4. Шеферд Джордж. Программирование на Microsoft Visual C++.NET. Мастер-класс./Пер. с англ. – 2-е изд. – М.: Издательско-торговый дом «Русская Редакция»; СПб.: Питерб 2005. – 928 стр.: ил.

 


Дата добавления: 2018-08-06; просмотров: 853; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!