Михаил Краснов - Графика DirectX в Delphi
Один из способов решения задачи таков: при установке программы в файл автозагрузки дописывается строка, объявляющая каталог устанавливаемой программы доступным для всех приложений. Теперь при каждом поиске файла система будет заглядывать и в этот каталог. Подобное решение малоэффективно и удовлетворительным являлось лишь два десятилетия назад, когда на одном компьютере установить больше десятка крупных программ практически было невозможно. Сегодня же на компьютере пользователя могут быть установлены одновременно сотни приложений, и блуждание по каталогам может оказаться чересчур долгим. К тому же библиотеки разных производителей, с различным набором функций, могут быть случайно названы одинаково, и клиенту первым может попасться не тот сервер, который он ищет.
Итак, клиент в любой момент должен иметь точную информацию о текущем расположении нужного ему сейчас сервера.
Найденное разработчиками решение состоит в использовании базы данных установленных программ. Такая база данных носит название реестр. Функции ее гораздо шире названной мною, но я сосредоточусь только на ней. При установке сервер записывает в реестр свой уникальный идентификатор и, как минимум, информацию о собственном физическом расположении. Клиент при вызове сервера обращается к базе данных установленных программ, ориентируясь по идентификатору, находит и загружает либо запускает сервер. Клиенту, в принципе, можно и не знать имени необходимой библиотеки, главное должен быть известен ее идентификатор. Схема взаимодействия клиента и сервера мною упрощена, напрямую они не общаются, но, надеюсь, основное я сумел донести.
Глобальный вопрос, мучающий впервые прикоснувшихся к этой теме, можно сформулировать так: "Почему это здесь?". DirectX является частью операционной системы, он неизбежно присутствует в ней сразу же после установки. Хоть он и реализован в виде набора файлов, но помещаются они всегда в системный каталог, и ничего зазорного в этом для системных файлов нет. Первый, но не самый главный, ответ на этот вопрос вы уже получили: разработчики стремились отразить требование сегодняшнего дня, связанное с поддержкой ООП на уровне операционной системы. Приступая к разработке DirectX, разработчики корпорации Microsoft задались целью создать набор объектно-ориентированных библиотек, и СОМ-модель подходит здесь как нельзя лучше.
Подчеркну, что данная книга не является официальным документом, все, что вы в ней читаете, является мыслями автора, и не более. Многие мысли основаны на официальных документах, но далеко не все.
Например, я твердо убежден, что DirectX можно было бы и не строить на основе СОМ-модели. Для обеспечения его функциональности технологии использования "обычных" библиотек вполне достаточно, а для графической части системы ООП является подспорьем незначительным. Тем более что в технологии СОМ имеются ограничения с точки зрения традиционного ООП, а новичкам изучение СОМ часто тяжело дается. Нередко для наглядности при изучении парадигмы ООП прибегают к визуальным иллюстрациям, но сама техника программирования компьютерной графики очень хорошо описывается и стародавним процедурным подходом.
Итак, если бы DirectX не был основан на СОМ, он в чем-то, может быть, и выиграл. Но это не значит, что весомых оснований в решении разработчиков построить DirectX именно на основе СОМ-технологии нет.
Существенное преимущество СОМ-серверов перед обычными библиотеками состоит в облегчении контроля версии сервера. С самого начала работы над DirectX его разработчики были убеждены в том, что одной версией они не ограничатся, и каждая последующая версия продукта будет снабжена новыми, дополнительными функциями. А некоторые прежние функции будут изменяться, например, в связи с устранением ошибок.
В случае с традиционными DLL каждое новое обновление продукта порождает у разработчиков массу проблем. Можно новые функции располагать в библиотеках с новым именем, а старые функции клиентами будут загружаться из прежних библиотек. Это плохо, поскольку влечет потери времени.
Если же новая версия сервера реализована физически в файлах с прежним названием, как серверу узнать, запрашивает ли клиент старую версию функции или новую? Ведь наряду с клиентами, появившимися после выхода новой версии сервера, его будут использовать и клиенты, созданные до появления новой версии. Эти клиенты ничего не знают о новых функциях и изменениях в реализации функций, носящих прежнее имя. Конечно, в библиотеку можно поместить информацию о версии продукта, но тогда в коде каждой функции надо хранить информацию о том, к какой версии сервера она относится. Если добавляется очень много функций, то все это выливается в массу проблем для разработчиков сервера и клиентов.
Вдобавок остается проблема с беспорядочным размещением файлов библиотек на диске: одни и те же файлы могут многократно копироваться на жестком диске в разные каталоги. Или поверх обновленной версии может быть установлена более старая.
Технология СОМ тем и отличается от традиционных библиотек, что хорошо приспособлена к решению проблемы контроля версии сервера. Хочу подчеркнуть, что все эти проблемы устранимы и в схеме традиционных DLL, но решения получаются громоздкими и способны привести к ошибкам. С использованием же технологии СОМ появляется гарантия, что сервер не будет установлен многократно, а клиент станет получать именно запрашиваемый набор функций.
СОМ-объекты
Как уже отмечалось, технология СОМ появилась вслед за возникшей потребностью программистов получить реализацию парадигмы ООП. В СОМ любая часть программного обеспечения реализует свои сервисы как один или несколько объектов СОМ.
СОМ-объекты представляют собой двоичные программные компоненты, подобно компонентам Delphi, устанавливаемым на уровне операционной системы и доступным для использования в любой среде программирования. СОМ-объекты для Object Pascal ничем, по сути, не отличаются от обычных объектов, или, по крайней мере, очень похожи на обычные невизуальные объекты, такие как объекты класса TBitmap. Изучение DirectX позволит нам разобраться с методами невизуальных объектов особых типов. Только необходимо сразу же запомнить, что у СОМ-объектов нет свойств, есть только методы. Вдобавок, коренное отличие таких объектов состоит в использовании конструкторов и деструкторов.
Для создания СОМ-объекта не вызывается функция конструктора, как для обычных объектов в Delphi. Первым нашим действием будет создание главного объекта, который имеет методы, использующиеся для создания других объектов и получения необходимых интерфейсов.
Для удаления СОМ-объекта вместо метода Free обычно предназначен метод _Release. Это справедливо в общем случае, но иногда для освобождения памяти, занятой СОМ-объектом, будем просто присваивать значение nil соответствующей переменной.
Интерфейсы
Интерфейсом обозначается набор функций, предоставляемый некоторым предложением. Обычные приложения предоставляют один интерфейс, т. е. весь тот набор функций, который реализован в вашей, к примеру, бухгалтерской программе, является в такой терминологии единым интерфейсом. Если бы ваша бухгалтерская программа могла предоставлять несколько наборов функций, то она имела бы несколько интерфейсов.
Здесь начинающие обычно испытывают затруднение. Вопрос, зачем же DirectX предоставляет несколько интерфейсов, кажется резонным.
Вспомним еще раз проблему контроля версии. Клиент может запрашивать функцию или набор функций, реализованных в различных версиях DirectX, по-разному. Очень важно предоставлять ему эти функции именно в той реализации, как он того ожидает. Например, если в какой-либо предыдущей версии функция реализована с известной ошибкой, то клиент при использовании этой функции может делать поправку на данную ошибку. Тогда, если клиент получит уже скорректированную функцию, такая поправка может только испортить все дело.
DirectX предоставляет несколько интерфейсов, связанных с различными версиями. Клиент запрашивает именно тот интерфейс, который ему известен. Например, клиент создан пару лет назад и просто ничего не знает о новых функциях, появившихся в DirectX с тех пор. Функции, чьи имена не изменились, но реализация в последующих версиях сервера претерпела изменения, должны работать именно так, как они работали во времена создания клиента, и как того ожидает клиент.
Конечно, подобная схема отнюдь не идеальна. Например, если функция в новой версии реализована эффективнее, то "старый" клиент просто не сможет ею воспользоваться, он запустит ее старую версию. Поэтому при установке новой версии DirectX не приходится ожидать, что ранее установленные игры автоматически станут выглядеть иначе. Но все же это одно из самых эффективных решений.
Итак, сервер поддерживает один или несколько интерфейсов, состоящих из методов. Клиенты могут получить доступ к сервисам только через вызовы методов интерфейсов объекта. Иного непосредственного доступа к данным объекта у них нет.