Статья
Версия для печати
Обсудить на форуме
Введение в COM


Сегодня мы хотим предложить вашему вниманию первую статью цикла по СОМ-технологиям.

У всех на слуху это сочетание слов: СОМ-технология, СОМ-программирование. Но многие ли могут похвастаться тем, что принадлежат к числу людей, которые себе абсолютно четко представляют, что стоит за этими словами? Я, во всяком случае, к таковым не отношусь. Думаю, что и многие из вас также составляют мне походящую компанию в данном вопросе.

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

Итак, прошу любить и жаловать:

В роли чайника задающего вопросы - Never

В роли отвечающего мэтра -Alf.

(Долгие и продолжительные апплодисменты.)

СОМ-посиделки с Альфом-1.

Попытаемся начать с самого начала. Что означает аббревиатура COM?

COM - это Component Object Model, т.е. Компонентная Объектная Модель. Модель эта разработана фирмой Microsoft и верой и правдой служит разработчикам программного обеспечения уже более 10 лет.

Справедливости ради следует заметить, что COM не является единственной доступной в настоящее время объектной моделью, и даже, пожалуй, не является самой развитой из имеющихся. Существуют альтернативные модели, например, CORBA, которая поддерживается OMG (Object Management Group) и реализована на различных аппаратных и программных платформах.

COM реализована в среде операционных систем семейства Microsoft Windows. Периодически появляются слухи о попытках переноса COM на другие платформы, например, Linux или MacOS, но пока что мне неизвестны какие-либо существенные успехи в этом направлении.

В чем секрет столь устойчивого положения COM среди конкурирующих моделей? Он достаточно прост - COM имеет эталонную реализацию от Microsoft. Это обеспечивает высокую степень совместимости компонентов, написанных различными производителями ПО. Проблемы совместимости весьма актуальны, например, для той же CORBA, которая при ее отсутствии в принципе могла бы составить весьма существенную конкуренцию COM.

Объектная модель Microsoft возникла не сразу, она развивалась более 10 лет, время от времени меняя название и отпочковывая дополнительные ветви. Встретив в литературе аббревиатуры OLE, OLE2, COM, DCOM, COM+, ActiveX, ...(кажется, ничего не забыл), имейте в виду, что все это - различные ипостаси COM.

Зачем нужно или можно использовать СОМ, в каких случаях, какие преимущества даст использование, где она, соответственно не работает или работает не так, как надо - т.е. где ее нежелательно применять.

Идея COM возникла не на пустом месте.

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

Как традиционно в общем случае решается проблема дефицита? При помощи соответствующей технологии, разумеется. Например, изобретение ткацких станков в Англии сделало ткань дешевле, доступнее и, главное, качественнее домотканого холста. Автомобиль из средства роскоши превратился в общедоступный предмет после того, как вместо полукустарного штучного производства на заводах Форда заработал конвейер. Аналогичных примеров можно привести тьму, но их объединяет общая идея: чтобы сделать что-то доступным, нужно располагать технологией его создания.

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

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

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

Подход с использованием объектных библиотек, безусловно, прогрессивнее, но тоже не решает всех проблем. А основных проблем здесь я вижу две. Первая из них - различные языки используют различные механизмы передачи параметров подпрограммам. Так, например, FORTRAN-IV традиционно использует передачу параметров только по ссылке, и среди известных мне реализаций попадались довольно причудливые по способам хранения этих ссылок. Большинство других языков для передачи параметров подпрограммам используют стек, но полного единодушия здесь тоже не наблюдается. Одни помещают в стек параметры слева направо, другие - справа налево. Одни чистят за собой стек самостоятельно, другие возлагают эту задачу на вызывающую программу. Те, кто пишет на языке C, знают не понаслышке, что такое соглашения о вызовах cdecl, stdcall и т.п. Вторая серьезная проблема - это невозможность проверить соответствие количества и типов фактических параметров формальным при вызове подпрограммы как на этапе компиляции, так и во время выполнения. Подобные ошибки происходят довольно часто по небрежности либо из-за плохого качества документации к библиотекам и приводят к весьма неожиданным и непредсказуемым сбоям во время выполнения программы.

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

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

Основная особенность COM - это независимость от языка программирования. Нередко встречается ситуация, когда клиентское приложение, написанное на Visual Basic, использует компоненты, созданные посредством Visual C++. Для достижения этой независимости в COM имеются собственный механизм передачи параметров и собственная система типов, нейтральные по отношению к используемым языкам программирования.

Остановимся подробнее на независимости от языка программированияКак это реализовано? За счет чего? Похоже на то, как это решили в Java? (в общих чертах, естественно).

Начну, пожалуй, с последнего вопроса. Нет, на Java это совсем не похоже. В первую очередь потому, что в Java вопрос независимости от языка программирования, насколько я знаю, не решили. Более того  даже и не пытались решать. Во главу угла в Java поставлена кросс-платформенная переносимость (эта идея появилась еще в конце 60-ых годов прошлого столетия вместе с Unix и до сих пор не дает покоя все новым поколениям разработчиков, - некое подобие святого Грааля, столь же неуловимое). Т.е. работающее Java-приложение должно оставаться таковым в любом программном и операционном окружении (в разумных пределах, разумеется), при этом вопрос взаимодействия с модулями, написанными на других языках, не считался основным (и правильно, иначе никакой переносимости в результате и не получилось бы).

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

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

Тип данных VARIANT наверняка знаком тем, кто имеет опыт работы с MS Visual Basic, поскольку из всех систем программирования из состава MS Visual Studio именно VB имеет наибольшую ориентацию в направлении СОМ. Переменная типа VARIANT может хранить практически что угодно: логическое, целочисленное или действительное значение, дату, указатели на них, на массив или интерфейс и т.п. Причем такая переменная хранит не только значение, но и знает, к какому типу оно относится. Это позволяет наладить контроль типов на этапе выполнения, поскольку компилятор не знает, что на самом деле окажется в этой переменной в дальнейшем.

Физическая реализация типа VARIANT весьма проста. Если посмотреть ее описание на языке С, то мы увидим простую структуру с двумя полями (на самом деле их больше, но остальные не используются и являются зарезервированными, так что на них можем не обращать внимание): тег (VARTYPE vt), который хранит информацию о типе содержимого переменной (VARTYPE  - это перечисление всевозможных подтипов VARIANT), и объединение (union), в котором собраны воедино все эти подтипы.

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

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

СОМ  это модель. Т.е. как модель она должна претендовать на некую универсальность, быть чему-то шаблоном. Какие ОБЩИЕ задачи позволяет выполнять технология?

В качестве модели COM определяет набор правил, по которым должны строиться компоненты. Только при их строгом соблюдении компоненты обеспечивают корректное и надежное функционирование.

Также COM регламентирует способы использования компонентов из клиентских программ

Наиболее общие задачи, которые позволяет выполнить COM, не являются уникальными для этой технологии. Это довольно тривиальная последовательность: создание необходимых объектов, их использование и последующее уничтожение с освобождением выделенных ресурсов. Подобным образом работают практически все программные продукты. Особенность COM состоит как раз в том, КАК это делается.

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

Разнообразие разновидностей COM возникло скорее из-за разнообразия решаемых задач, а не из-за каких-либо пороков, присущих самой модели.

Так, например, область ActiveX - это соответствующие элементы управления, которые выполняются в контексте вызвавшего их процесса (так называемые in-proc servers). Зачастую эти элементы имеют графический интерфейс (всевозможные кнопочки, окошки и т.п.), хотя это и не обязательно, попадаются элементы, лишенные визуального представления, вроде ADO. Типичный пример  палитра инструментов Visual Basic 6.0, изобилующая элементами управления ActiveX, которые достаточно перетащить мышкой на окно формы.

OLE Automation  другой раздел COM. Здесь клиент уже управляет самостоятельным приложением, которое выполняется в контексте собственного процесса, в изолированной области памяти. Разумеется, управлять таким способом можно далеко не каждым приложением. Оно должно быть написано специальным образом, в виде сервера Automation. К счастью, очень многие популярные приложения написаны именно как сервера COM, включая Microsoft Office, Microsoft Visio, программы семейства Corel Draw, интегрированная оболочка Visual Studio и т.д. Таким образом, у программиста появляется выбор, скажем, реализовывать сложное оформление и вывод документа на печать самостоятельно или же воспользоваться для этой цели тем же MS Word, управляя им посредством OLE Automation.

Еще один пример  DCOM, т.е. Distributed COM, - распределенная разновидность COM, способная работать в локальной сети. Используя DCOM, можно, например, воспользоваться компьютером соседа и выполнить расчет таблицы в среде Excel, при этом будут задействованы ресурсы другого компьютера, а вам останется только дождаться готового результата и получить его в свое распоряжение. Таким образом, можно строить распределенные приложения, в которых специально выделенные серверы приложений, оснащенные соответствующим образом, решают определенные задачи по запросам клиентов и выдают им готовые результаты.

Таким образом, наличие нескольких ветвей или, если угодно, направлений в COM свидетельствует не о беспомощности технологии как таковой, а скорее о широком спектре задач, решаемых ее посредством. Впрочем, при всем этом COM не является простой свалкой нескольких технологий, собранных вместе под одним названием. Как мы, наверное, сможем убедиться при дальнейшем обсуждении, все эти разновидности имеют в своей основе общую идею, которая позволяет считать их именно частными случаями единой технологии.

Выше упоминался сервер СОМ Что это такое?

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

Приложение, которое написано специальным образом и может управляться извне, называется сервером автоматизации OLE (Automation Server). Например, приложения из состава MS Office являются серверами автоматизации, что делает их намного полезнее обычного текстового редактора или табличного процессора. Также управляемыми через OLE Automation являются Visual Studio, MS Visio, пакет Corel Draw, почтовые программы и многие другие популярные приложения.

Существуют библиотеки компонентов СОМ? Как в них разбираться? Как выяснить  что именно тебе нужно и правильно выбрать существующий компонент?

Компоненты от Microsoft, как правило, хорошо документированы, в частности в MSDN. А в общем случае они не отличаются от обычных программ  попадаются как экземпляры с небрежной и неточной документацией, с которыми приходится разбираться методом проб и ошибок, так и блестяще документированные компоненты. Этот вопрос всецело относится к совести разработчиков.

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

Если есть модель, то на ее основе можно строить свои, пользовательские компоненты Как это можно делать? Нужно ли это делать? И как часто бывает нужно создавать свои компоненты?

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

Хотя для разработки компонентов имеются соответствующие инструментальные средства (например, библиотека ATL для MS VC++), все же дело это не слишком простое, требующее и знаний, и определенного уровня программистского мастерства, и затрат труда, конечно. Идти на эти затраты оправдано в том случае, если очевидно, что компонент будет в дальнейшем использован еще не раз. В противном случае лучше оформить соответствующий модуль традиционным образом - в виде набора классов и/или подпрограмм - и не мучиться с компонентами.

Итак, мы хотим использовать СОМ-технологию. С чего нам надо начать?

На практике, чтобы проникнуться духом СОМ, я бы порекомендовал начать с написания простейшего клиента, который обращается к услугам одного из готовых серверов, например, MS Excel. Я считаю, что проще всего это сделать в среде MS Visual Basic V6, хотя при хорошем знакомстве с С++ можно воспользоваться и им. Правда, VB скроет от программиста большинство деталей взаимодействия клиента и сервера, в то время как в С++ все они будут как на ладони. Наверное, правильным было бы попробовать оба способа, но начать все же с VB.

Итак, первоначальное поверхностное знакомство с СОМ будем считать законченным (Продолжение следует)
Версия для печати
Обсудить на форуме