Работа с библиотеками динамической компоновки (DLL). Создание проекта библиотеки динамической компоновки (DLL)

Библиотеки динамической компоновки

В этой главе мы расскажем вам о важнейшем механизме, лежащем в основе операционной системы Windows - механизме библиотек динамической компоновки DLL. Если вы уже заметили, в Windows имеется много важнейших механизмов и систем, например, только что рассмотренная система управления памятью, интерфейс графических устройств GDI, система динамического обмена данными DDE, система управления шрифтами, интерфейсы для мультимедиа, система динамической вставки и привязки объектов OLE, и так далее, и так почти до бесконечности.

Однако будем терпеливыми, и постараемся сосредоточиться, так как вся операционная система Windows и все ее драйверы (кроме виртуальных), а также другие расширения в некотором смысле есть ни что иное, как набор библиотек динамической компоновки. Редкое крупное приложение Windows обходится без собственных библиотек динамической компоновки, и ни одно приложение не может обойтись без вызова функций, расположенных в таких библиотеках. В частности, все функции программного интерфейса Windows находятся именно в библиотеках динамической компоновки DLL (Dynamic-Link Libraries).

Что же это за библиотеки и почему они имеют такое большое значение?

3.1. Статическая и динамическая компоновка

Вспомним старые добрые времена, когда операционная система MS-DOS и компьютеры с памятью 1 Мбайт удовлетворяли запросы подавляющего большинства пользователей. Как создавался загрузочный файл программы для MS-DOS? Вы готовили исходный текст приложения на своем любимом языке программирования (Си, Паскаль, Модула-2 и т. д.), затем транслировали его для получения объектного модуля. После этого в дело включался редактор связей, который компоновал объектные модули, полученные в результате трансляции исходных текстов и модули из библиотек объектных модулей, в один exe-файл. В процессе запуска файл программы загружался полностью в оперативную память и ему передавалось управление.

Таким образом, редактор связей записывал в файл программы все модули, необходимые для работы. Для обращения к внешним по отношению к программам модулям операционной системы MS-DOS и модулям BIOS использовался механизм программных прерываний. В любой момент времени в оперативной и постоянной памяти компьютера находился весь код, необходимый для работы как запущенной программы, так и самой операционной системы MS-DOS.

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

В среде мультизадачной операционной системы, такой как Windows, OS/2 или UNIX, статическая компоновка неэффективна, так как приводит к неэкономному использованию очень дефицитного ресурса - оперативной памяти. Представьте себе, что в системе одновременно работают 5 приложений, и все они вызывают такие функции, как sprintf, memcpy, strcmp и т. д. Если приложения были собраны с использованием статической компоновки, в памяти будут находится одновременно 5 копий функции sprintf, 5 копий функции memcpy, и т. д (рис. 3.1). А ведь программы могут использовать и более сложные функции, например, предназначенные для работы с диалоговыми панелями или масштабируемыми шрифтами!

Рис. 3.1. Вызов функций при использовании статической компоновки

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

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

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

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

Рис. 3.2. Вызов функции при использовании динамической компоновки


В операционной системе Windows версии 3.1 файлы библиотек динамической компоновки (DLLбиблиотеки) имеют расширение имени dll, хотя можно использовать любое другое, например, exe.

В первых версиях Windows DLL-библиотеки располагались в файлах с расширением имени exe. Возможно поэтому файлы krnl286.exe, krnl386.exe, gdi.exe и user.exe имеют расширение имени exe, а не dll, не смотря на то, что перечисленные выше файлы, составляющие ядро операционной системы Windows, есть ни что иное, как DLL-библиотеки.

Механизм динамической компоновки используется не только в системе Windows. Более того, он был изобретен задолго до появления Windows. Например, в мультизадачных многопользовательских операционных системах VS1, VS2, MVS, VM, созданных для компьютеров IBM-370 и аналогичных (вспомните добрым словом ушедшую в прошлое серию ЕС ЭВМ и операционные системы SVS, TKS, СВМ, МВС), код функций, нужных параллельно работающим программам, располагается в отдельных библиотеках и может загружаться при необходимости в специально выделенную общую область памяти. Операционная система OS/2 также работает с DLL-библиотеками.

3.2. DLL-библиотеки в операционной системе Windows

Формат DLL-библиотеки почти идентичен формату загрузочного модуля приложения Windows, однако вы не можете "запустить" DLL-библиотеку на выполнение, как обычное приложение. И это понятно, так как назначение DLL-библиотек другое - они служат хранилищем функций, вызываемых приложениями во время работы. Функции, расположенные в DLL-библиотеках, могут вызывать функции, которые находятся в других библиотеках (рис. 3.3).

Рис. 3.3. Вызов функций из DLL-библиотек

Для того чтобы вам были понятны отличия между приложением и DLL-библиотекой, уточним такие понятия, как задача (task), копия приложения (instance) и модуль (module).

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


Рис. 3.4. Копии приложения и модуль приложения

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

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

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

DLL-библиотека также является модулем. Она находится в памяти в единственном экземпляре, содержит сегменты кода и ресурсы, а так же один сегмент данных (рис. 3.5). Можно сказать, что для DLL-библиотеки создается одна копия (instance), состоящая только из сегмента данных, и один модуль, состоящий из кода и ресурсов.

Рис. 3.5. Структура DLL-библиотеки в памяти

DLL-библиотека, в отличие от приложения, не имеет стека и очереди сообщения.

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

Тем не менее, функции, расположенные в DLL-библиотеке, пользуются сегментом данных, принадлежащей этой библиотеке, а не копии приложения. При этом возникают трудности, связанные с тем, что сегментный регистр SS, с помощью которого адресуется стек, указывает на стек копии приложения, а регистр DS - на сегмент данных DLL-библиотеки. В обычных приложениях Windows регистры SS и DS адресуют один и тот же автоматический сегмент данных. Позже мы займемся этим вопросом вплотную.

Для чего используются DLL-библиотеки?

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

С помощью DLL-библиотек можно организовать коллективное использование ресурсов или данных, расположенных в сегменте данных библиотеки. Более того, вы можете создать DLLбиблиотеки, состоящие только из одних ресурсов, например, из пиктограмм или изображений bitmap. В состав Windows входит DLL-библиотека moricons.dll, состоящая из одних пиктограмм. Файлы с расширением fon представляют собой ни что иное, как DLL-библиотеки, содержащие шрифты в виде ресурса.

Функции, входящие в состав DLL-библиотеки, могут заказывать блоки памяти с атрибутом GMEM_SHARE. Такой блок памяти не принадлежит ни одному приложению и поэтому не освобождается автоматически при завершении работы приложения. Так как в Windows версии 3.1 все приложения используют общую глобальную память, блоки памяти с атрибутом GMEM_SHARE можно использовать для обмена данными между приложениями. Управлять таким обменом могут, например, функции, расположенные в соответствующей DLL-библиотеке. Однако в следующих версиях Windows каждое приложение будет работать в собственном адресном пространстве, поэтому для организации обмена данных между приложениями следует использовать специальный механизм динамического обмена данными DDE, который мы рассмотрим позже в отдельной главе.

Использование DLL-библиотек повышает модульность приложений и самой операционной системы Windows. С точки зрения приложения DLL-библиотека является не более чем набором функций с тем или иным интерфейсом, а также, возможно, набором ресурсов. Внутреннее

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

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

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

Даже если вы не собираетесь обрабатывать или вызывать прерывания и не разрабатываете собственный драйвер, отдельные подсистемы большого приложения имеет смысл оформлять в виде DLL-библиотек из соображений модульности и доступности библиотек для других приложений. Например, в приложении SMARTPAD мы создали орган управления Toolbar с использованием разработанного нами класса С++ Toolbar. Однако если бы мы сосредоточили все функции этого класса в DLL-библиотеке, нашим органом управления могли бы воспользоваться и другие созданные нами приложения.

Разумеется, у динамической компоновки есть и свои недостатки.

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

Во-вторых, в дополнение к exe-файлу вместе с приложением необходимо устанавливать один или несколько dll-файлов, что в некоторой степени усложняет процесс установки и сопровождения приложения. Может также возникнуть ситуация, при которой приложение не находит свою DLLбиблиотеку, несмотря на то, что нужная библиотека есть на диске.

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

3.3. Структура DLL-библиотеки

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


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

Функция LibEntry

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

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

Задачей функции LibEntry является инициализация локальной области памяти, если она определена для DLL-библиотеки.

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

Регистры

Описание

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

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

Требуемый размер локальной области данных, указанный в операторе HEAPSIZE файла определения модуля DLL-библиотеки. При вызове функции инициализации локальной области памяти LocalInit в качестве значения параметра uStartAddr следует использовать нуль, а в качестве значения параметра uEndAddr - содержимое регистра CX

Дальний указатель на строку параметров, которая может быть указана при явной загрузке DLL-библиотеки (позже мы рассмотрим различные способы загрузки DLL-библиотеки). Обычно этот параметр не используется

Вам не надо определять функцию LibEntry самостоятельно, так как при создании файла DLLбиблиотеки редактор связей, входящий в систему разработки, включит уже имеющийся в стандартной библиотеке модуль. Этот стандартный модуль выполняет всю необходимую работу по инициализации локальной области памяти DLL-библиотеки (с помощью функции LocalInit) и затем вызывает функцию LibMain, которая будет описана в следующем разделе.

Иногда для DLL-библиотеки может потребоваться нестандартная инициализация. Только в этом случае вам придется разработать функцию LibEntry самостоятельно. За основу вы можете взять файл libentry.asm из SDK, который содержит исходный текст упомянутой выше функции.

Функция LibMain

Функция LibMain должна присутствовать в каждой стандартной DLL-библиотеке. Эту функцию вам надо определить самостоятельно, причем вы можете воспользоваться языком программирования С.

По своему назначению функция LibMain напоминает функцию WinMain обычного приложения Windows. Функция WinMain получает управление при запуске приложения, а функция LibMain - при загрузке DLL-библиотеки в память. Так же как и функция WinMain, функция LibMain имеет параметры, которые можно использовать для инициализации библиотеки.

Приведем прототип функции LibMain:

int FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,

WORD wHeapSize, LPSTR lpszCmdLine);

Параметр hInstance при вызове функции содержит идентификатор DLL-библиотеки. Это ни что иное, как содержимое регистра DI перед вызовом функции LibEntry.

Через параметр wDataSegment передается селектор, соответствующий сегменту данных DLLбиблиотеки. Если DLL-библиотека не имеет сегмента данных, этот параметр содержит идентификатор DLL-библиотеки (точнее, идентификатор модуля DLL-библиотеки). Значение параметра wDataSegment соответствует содержимому регистра DS перед вызовом функции

Параметр wHeapSize содержит размер в байтах локальной области данных DLL-библиотеки. Если DLL-библиотека не имеет сегмента данных, в этом параметре находится нулевое значение.

И, наконец, через параметр lpszCmdLine передается указатель на командную строку, которую можно передать DLL-библиотеке при ее явной загрузке в память.

Если инициализация DLL-библиотеки выполнена успешно, функция LibMain должна возвратить ненулевое значение. Если в процессе инициализации произошла ошибка, следует возвратить нуль. В этом случае функция LibEntry также возвратит нуль. Это приведет к тому, что Windows выгрузит библиотеку из памяти.

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

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

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

Перед завершением работы приложение должно вызвать созданную вами функцию, освобождающую выделенную структуру данных. Она может называться, например, LibUnregister.

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

Приведем пример простейшего варианта функции LibMain:

int FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, WORD wHeapSize, LPSTR lpszCmdLine)

if(wHeapSize != 0) UnlockData(0);

Эта функция проверяет размер локальной области данных, и если эта область данных существует, она расфиксируется при помощи макрокоманды UnlockData. После этого возвращается признак успешной инициализации.

Функция WEP

DLL-библиотека в любой момент времени может быть выгружена из памяти. В этом случае Windows перед выгрузкой вызывает функцию WEP . Эта функция, как и функция LibMain, вызывается только один раз. Она может быть использована для уничтожения структур данных и освобождения блоков памяти, заказанных при инициализации DLL-библиотеки.

Приведем прототип функции WEP (при использовании системы разработки Borland Turbo C++ for Windows):

int FAR PASCAL WEP(int bSystemExit);

Параметр bSystemExit может принимать значения WEP_FREE_DLL и WEP_SYSTEM_EXIT . Этот параметр указывает причину выгрузки DLL-библиотеки из памяти. В первом случае выгрузка выполняется потому, что либо функциями библиотеки не пользуется ни одно приложение, либо одно из приложений выдало запрос на выгрузку. Если же параметр имеет значение WEP_SYSTEM_EXIT, выгрузка библиотеки происходит из-за завершения работы операционной системы Windows.

Функция WEP должна всегда возвращать значение 1.

Иногда при запуске какой-либо программы появляется сообщение, что не найден файл *.dll . Для операционных систем Microsoft Windows, большая часть функциональных возможностей операционной системы обеспечивается библиотеками динамической компоновки (DLL). Кроме того, некоторые возможности программ могут быть реализованы в библиотеках DLL. Например некоторые программы могут содержать много различных модулей и при работе использовать только часть из них. Таким образом операционная система и программы загружаются быстрее, работают быстрее и занимают меньше места на диске компьютера.
Что такое DLL?
DLL — это библиотека, содержащая код и данные, которые могут использоваться несколько программами одновременно. Например, в операционных системах Windows, библиотека Comdlg32.dll выполняет общие функции, связанные с диалоговыми окнами. Таким образом каждая программа может использовать функцию, которая содержится в этой библиотеке для реализации диалогового окна Открыть. Это позволяет повысить уровень повторного использования кода и эффективного использования памяти.
С помощью библиотек можно реализовать модульность для программы, в виде отдельных компонентов. Например бухгалтерскую программу можно продать по модулям. Каждый модуль может быть загружен в основной программе во время выполнения установки. Отдельные модули загружается только при запросе функций заложенных в них, поэтому загрузка программы выполняется быстрее.
Кроме того обновления легче применить для каждого модуля, не влияя на другие части программы. Например имеется программа по зарплате и надо изменить налоговые ставки за каждый год. Когда эти изменения изолированы в библиотеке, можно применить обновления без необходимости построения или установки программы целиком. Давайте рассмотрим пример создания библиотеки с самыми простыми математическими методами, такие как произведение, деление, сумма и разность.
Для начала, создадим новый проект, для этого запустите Microsoft Visual Studio и перейдите в меню Файл -> Создать -> Проект… или выполните сочетание клавиш Ctrl+Shift+N .


В открывшемся диалоговом окне выберете версию Framework, а в левой части «Установленные шаблоны» выберете «Visual C#» , в центральной части вам будет представлен список шаблонов, выберете «Библиотека классов» и введите имя библиотеки, можно оставить по умолчанию.


У вас откроется вкладка с классом по умолчанию.


По умолчанию создается класс Class1 , переименуем его в класс Calculator. Для этого перейдите в меню Вид - > Классы или выполните сочетание клавиш Ctrl + W , с последующем нажатием клавиши C .

В правой части программы у вас откроется вкладка «Окно классов» .

Выберете по умолчанию созданный класс Class1, сделайте клик правой клавишей мыши по нему и выберете «Переименовать… ».


В открывшемся окне введите новое имя класса Calculator и нажмите кнопку OK, обратите внимание, что данное окно позволяет переименовать класс во всем проекте.


После нажатия клавиши OK, вам будет предложено просмотреть список изменений, которые будут внесены в проект, а так же просмотр изменений кода.

Добавим в класс Calculator несколько методов и добавим к ним описание.
namespace ClassLibrary1 { /// /// Математический класс /// public class Calculator { /// /// Метод возвращает сумму двух целых чисел /// /// /// /// public static int Summ(int firstNumber, int secondNumber) { return firstNumber + secondNumber; } /// /// Метод возвращает разность двух целых чисел /// /// /// /// public static int Division(int firstNumber, int secondNumber) { return firstNumber - secondNumber; } /// /// Метод возвращает произведение двух чисел /// /// /// /// public static long Multiply(long x, long y) { return (x * y); } /// /// Метод возвращает деление двух чисел /// /// /// /// public static int Residual(int firstNumber, int secondNumber) { return (firstNumber / secondNumber); } } } По умолчанию для всех проектов стоит режим построения Debug (режим отладки), переведем проект в режим построения конечной версии (Release ). Для этого перейдите в обозреватель решений и сделав клик правой клавишей мыши по названию проекта, выберете в открывшемся контекстном меню пункт «Свойства ».


В открывшемся окне «Страницы свойств Решение “ClassLibrary1 ”» выберете везде конфигурацию Release, как показано на скриншоте ниже.


Наверно все замечали, что при наборе кода, появляется подсказка к методам или функциям. Мы задали такую подсказку в тегах . Но если сейчас просто выполнить построение библиотеки, то при подключении к другим проектам никаких подсказок видно не будет. Что бы устранить данную проблему, нам необходимо сформировать XML файл документации к проекту. Для этого в обозревателе решений выполните клик правой клавишей мыши по названию библиотеки и в открывшемся контекстном меню выберете пункт «Свойства ».


После этих действий у вас откроется новая вкладка со свойствами проекта ClassLibrary1 . Выберете в вкладку «Построение » и найдите раздел «Вывод», там вам будет предложено ввести путь куда будет выполнено построение конечной версии библиотеки и пункт необходимый нам для построения xml файла документации, тут вам необходимо просто поставить галочку как показано на скриншоте ниже. Тут важно чтобы библиотека и файл документации находились в одном месте, поэтому проверьте чтобы их путь вывода совпадал.


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


После этого, выполним построение решения. Нажав на клавиатуре клавишу F6.


Как только программа закончит построение, можно перейти в директорию с проектом и посмотреть что получилось. На этом этапе закончилось создание библиотеки.


Для проверки работоспособности библиотеки создадим тестовый проект. Выполните Файл -> Создать -> Проект…


Выберете из предложенных шаблонов, шаблон «Приложение Windows Forms Visual C#». Задайте имя проекта и нажмите кнопку OK.


После создания проекта, в обозревателе решений сделайте клик правой клавишей мыши по разделу «Ссылки » и выберете в появившемся контекстном меню пункт «Добавить ссылку… ».


Выберете вкладку «Обзор» и укажите вашу библиотеку.


Если вы все успешно выполнили, в разделе «Ссылки » у вас появится название вашей библиотеки.

2.1.3.Библиотеки динамической компоновки – DLL. COM-модель и COM-объекты.

DLL

Файлы DLL (Dynamic Link Library, библиотека динамической компоновки) являются основой программной архитектуры Windows и отличаются от исполняемых файлов фактически только заголовком. Правда, это не означает, что если переименовать DLL-файл, то он станет исполняе-мым: имеется в виду заголовочная информация файла.

Для загрузки операционной системы необходимо запустить файл, имеющий размер всего 20 Кбайт. Как легко догадаться, в файл такого раз-мера невозможно поместить код, реализующий всю ту гигантскую работу, которая производится по ходу выполнения любого приложения. Этот файл является загрузчиком ядра операционной системы, физически размещен-ным в нескольких DLL-файлах.

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

Для создания любой программы Windows, имеющей соб-ственное окно, в проекте необходимо подключать как минимум два модуля: Windows и Messages. Первый из этих файлов содержит прототипы функций API и GDI. Посмотрим на прототип одной из них:

function CreateDC; external gdi32 name "CreateDCA";

Здесь величина gdi32 - константа, описанная в этом же модуле:

Const gdi32 = "gdi32.dll";

Таким образом, функция CreateDC физически размещена в файле gdi32.dll и каждое приложение, использующее функции GDI, обращается к этой библиотеке.

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

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

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

Иногда программисту бывает необходи-мо просмотреть список функций и процедур, размещенных в конкретном файле DLL. Для этого можно воспользоваться утилитой tdump.exe, поставляемой в составе Delphi (в каталоге bin). Для ее использования скопируйте ее и необходимый dll-файл в отдельный каталог и запустите утилиту с параметрами <имя анализируемого файла> и <имя файла-результата>, например:

TDUMP.EXE gdi32.dll gdi.txt

В файле gdi.txt будет выдана информация, извлеченная утилитой из заголовка библиотеки. Информация группируется по секциям, среди которых наиболее часто программисту требуется секция экспортируемых функций для уточне-ния содержимого библиотеки.

Итак, чаще всего DLL представляет собой набор функций и процедур. Как говорится в справке Delphi по DLL, "динамические библиотеки являются идеалом для многоязыковых проектов". Это действительно так: при исполь-зовании DLL совершенно безразлично, в какой среде созданы сама биб-лиотека и вызывающие ее модули.

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

Приведем несложный пример библиотеки:

library MyDLL; //проект библиотеки. Файл должен иметь то же имя.

//описание экспортируемой функции

procedure MySquare (var x: integer); export; stdcall;

exports //список экспортируемых функций

//блок инициализации библиотеки

После компиляции образуется файл MyDLL.DLL. Таким образом, сервер готов. Теперь создадим клиента:

procedure MySquare (var x:integer); stdcall; external "MyDLL.dll";

ShowMessage(IntToStr(x));

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

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

// процедурный тип подгружаемой функции

type TMySquare = procedure (var x:integer); stdcall;

procedure TForm1.Button1Click (Sender: TObject);

hcDLL: THandle; //указатель на библиотеку

ProcMySquare: TMySquare; //подгружаемая функция

hcDLL:=LoadLibrary("MyDLL.dll"); //Динамически загружаем DLL

if hcDLL<=HINSTANCE_ERROR then //Проверка на наличие библиотеки

ShowMessage("Не найдена библиотека MyDLL.dll.");

//библиотека загружена. Получаем адрес точки входа функции.

ProcMySquare:=GetProcAddress(hcDLL, "MySquare");

if not Assigned(procMySquare) then //Проверка на наличие функции

ShowMessage("Функция не найдена.");

procMySquare(x);

ShowMessage(IntToStr(x));

FreeLibrary(hcDLL); //Выгружаем библиотеку

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

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

COM-модель

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

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

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

Важно уяснить, что COM – это не язык, не протокол. Это метод взаимодействия между программами и способ создания программ.

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

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

COM-объекты

COM-объекты представляют собой двоичные программные компоненты, подобные невизуальным компонентам Delphi, устанавливаемые на уровне операционной системы и доступные для использования в любой среде программирования. Основное отличие COM-объектов от других состоит в том, что у них нет свойств, а есть только методы. К тому же, еще одно отличие состоит в методах использовании конструкторов и деструкторов.

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

Для удаления COM-объекта вместо метода Free обычно предназначен метод _Release.

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

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

Все COM-интерфейсы унаследованы от интерфейса, называемого IUnknown, обладающего тремя методами: QueryInterface, AddRef и _Release.

Последний в этом списке метод мы уже вскользь обсуждали – удаление объекта.

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

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

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

2.2.Обзор среды программирования Borland Cbuilder

2.2.1.Отличия Borland CBuilder и Borland Delphi. Особенности синтаксиса языка C++ в Borland Cbuilder.

(лекция не читалась)

2.2.2.Особенности разработки приложений в среде CBuilder

(лекция не читалась)

3.Технологии компьютерной графики

3.1.Разработка графических приложений без использования специализированных библиотек

3.1.1.Методы ускорения построения 2D изображений. Динамическая запись в видеопамять устройства.

(лекция не читалась)

3.1.2.Работа с изображениями. Форматы графических файлов (BMP, JPEG, GIF). Чтение и запись графических файлов.

(лекция не читалась)

3.2.Разработка графических приложений с использованием специализированных библиотек

3.2.1.Обзор библиотек OpenGL и DirectX

На протяжение многих лет среди программистов идет спор о преимуществах использования того или иного интерфейса для создания графики в компьютерных играх и других графических приложениях. Главные соперники в этой области - библиотеки OpenGL и DirectX. Индустрия до сих пор так и не сделала однозначный выбор в пользу того или иного API.

Общие сведения о OpenGL

Стандарт OpenGL (Open Graphics Library - открытая гра-фическая библиотека) был со-здан и утвержден в 1992 году ведущими фирмами в области разработки программного обеспечения как эффективный аппаратно-независимый интер-фейс, пригодный для реализации на различных платформах. Основой стандарта стала биб-лиотека IRIS GL, изначально разработанная фирмой Silicon Graphics Inc (SGI).

С начала 90-х годов OpenGL использу-ется в различных областях ин-дустрии и науки. Архитектура библиотеки получилась на-столько удачной, что уже на протяжении более десяти лет она остается стабильной и предсказуемой. OpenGL де-факто является стандартом в области программирования графики. Но в этом скрыт и ее недостаток. Комитет по пересмотру архитектуры (ARB) работает довольно медленно – любое изменение стандарта требует множества согласований, оформления документации и т.д. Как следствие, OpenGL развивает-ся очень медленно. Правда, до недавнего времени с этим не было особых проблем, поскольку изна-чально библиотека предназна-чалась для быстрых рабочих станций профессионального уровня, которые обновляют не так уж часто. Однако сейчас даже дешевые видеокарты за $100 превзошли уровень профессиональных монстров пяти-летней давности стоимостью в тысячи долларов. При этом об-новление их возможностей происходит в среднем раз в год. Фактически OpenGL не по-спевает за индустрией, и, что-бы получить доступ к новей-шим функциям видеокарт, иг-ровые разработчики вынужде-ны использовать так называе-мый механизм расширений (extensions).

На данный момент OpenGL прошла путь от версии 1.0 всего до версии 1.4. "Революционная" версия 2.0 находится в процессе стан-дартизации.

Общие сведения о DirectX

К моменту выхода Windows 95 большинство игр по-прежнему делалось под MS-DOS. Windows в те времена не пред-оставляла никаких возможностей для программирования игр. Много-численные уровни абстракции (введенные в целях совмести-мости и универсальности) де-лали доступ к звуковому и ви-деооборудованию весьма мед-ленным и неприменимым для игровых приложений. Поэтому было решено разработать биб-лиотеку, предоставляющую возможность прямого доступа к аппаратуре. Это позволило бы играм работать на приемлемой скорости и, как следствие, увеличило бы про-дажи Windows 95.

Вместо создания собствен-ного API Microsoft использовала разработку небольшой компа-нии RenderMorphic. По неофициальной версии, изначально API был выполнен авторами в рамках студенческо-го задания и в конечном итоге провалился на экзамене. Тем не менее, Microsoft интегрировала эту библиотеку в свой Game SDK (игровой комплект разработки). Корпорация подавала это как идеальное решение для программирования игр.

Однако то, что позже стало называться DirectX 1.0, не при-обрело широкой популярности. Библиотека оказалась медлен-ной, с большим количеством ошибок, с неудобной архитекту-рой и, кроме того, чрезмерно сложной.

Разумеется, Microsoft не со-биралась сдаваться и продол-жила развитие библиотеки с учетом пожеланий разработчи-ков игр. Первой более или ме-нее жизнеспособной версией была DirectX 3.0. Позже после-довали версии 5, 6 и 7 (четвер-той не было), Седьмая версия была воспринята разработчика-ми с интересом: она хорошо работала, ее интерфейсы были достаточно удобны в использовании. Восьмая версия при-несла интересные нововведения – вершинные и пиксельные шейдеры (специальные, обычно короткие программы, предназначенные для выполнения на графическом процессоре; ис-пользуются для расчета осве-щения, создания тех или иных спецэффектов и т.п.). Не-давно вышедший DirectX 9 явился развитием этого перспективного направления.

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

Кроме того, DirectX, помимо собственно графики, предлагает интерфейсы для работы со звуком, источникам ввода, мультимедиа и т.д.. У OpenGL таких функций нет - это чисто графическая библиотека.

Архитектура

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

Структура DirectX очень сильно отличается от OpenGL. DirectX основан на модели COM (Component Object Model). Как следствие, в отличие от простого вызова функций эта модель предполагает выполнение дополнительных действий, связанных с компонентной архитектурой DirectX. Такая архитектура имеет как достоинства, так и недостатки. В частности, код, в котором используются вызовы DirectX, обычно трудно назвать легко читаемым и понимаемым. Даже рисование простого треугольника требует огромного объема кода. Раз-работчики Microsoft, конечно, понимают это, поэтому для уп-рощения программирования ими создана отдельная биб-лиотека DirectX Common Files, которая скрывает часто ис-пользуемый код. Однако, даже она не спасает ситуацию.

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

Производительность

Вопрос производительности настолько же важен, насколько запутан и неясен. Дебаты на тему "Что быстрее - OpenGL или DirectX?" не утихают. При этом, как ни парадоксально, скорость обоих библиотек оди-накова.

Иначе и быть не может, по-тому что сейчас большинство функций реализованы напря-мую через аппаратные ускори-тели. Естественно, производи-тельность может различаться в зависимости от степени опти-мизации программного кода и используемой для тестирова-ния аппаратной платформы. Оптимизация аппаратных драй-веров тоже может внести свой вклад в преимущество той или иной библиотеки. Такие вещи достаточно тяжело предвидеть, поэтому хорошие игровые "движки" часто имеют две вер-сии: под OpenGL и под DirectX. Это приводит к тому, что срок разработки увеличивается, стоимость ее возрастает и, естественно, появля-ются ошибки. Однако, таковы реа-лии современного рынка.

Сравнение

В чем же, основное различие между биб-лиотеками? Прежде всего – в удобстве интерфейса, функциях, гибкости, перспективах разви-тия и области применения.

Начнем с функций. В последнее время все чаще появляются заявления вроде "DirectX 9 под-держивает пиксельные шейдеры, a OpenGL не поддерживает, поэтому все игры должны быть написаны под DirectX!" Такое мнение верно только отчасти. Действительно, если посмот-реть на стандарт OpenGL по-следней версии (1.4), в нем ни слова о шейдерах.

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

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

Данный механизм является не-удобным. В DirectX все проще: функциональность, либо под-держивается, либо не поддер-живается данной версией биб-лиотеки. Если нет – придется надеяться и ждать следующей версии. Однако случаи, когда аппаратные функции не ис-пользуются из-за того, что не были включены в версию DirectX, довольно редки – как уже говорилось, Microsoft тесно работает с производителями аппаратуры. С другой стороны, ждать версий DirectX приходится око-ло года, а в это время новые функции уже доступны через расширения OpenGL.

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

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

Объем кода, необходимого для написания простой про-граммы на DirectX, весьма ве-лик (варьируется от 200 до 800 строк). Microsoft активно пыта-ется уменьшить этот показа-тель, но пока ее усилия особого успеха не приносят. В OpenGL все существенно проще - для решения такой же задачи необходимо менее 50 строк кода.

Серьезным достоинством OpenGL является, прежде всего, то, что это "открытый стан-дарт". Любая компания, имею-щая аппаратную платформу, может купить лицензию у SGI и затем сделать собственную реализацию OpenGL. Измене-ния в OpenGL предлагаются, обсуждаются и утверждаются представителями различных компаний. Что касается DirectX, то здесь ситуация пря-мо противоположная. Только Microsoft может вносить какие-либо изменения в библиотеку. Иначе говоря, именно Microsoft в конечном итоге оп-ределяет все пути развития библиотеки, и если путь был выбран неверно, это может быть исправлено только в но-вой версии.

Итак, достоинства библиотек становятся наиболее очевидны при их использовании в разных (но в то же время пересекаю-щихся) прикладных областях. DirectX идеален для профессио-нальной разработки игр и муль-тимедийных приложений на платформе Windows. OpenGL ис-пользуется на высокопроизводи-тельных рабочих станциях, в на-учной сфере, в образовании, а также в любых проектах, где требуется переносимость приложений на различные программные или аппаратные платформы. Кроме того, OpenGL применяется и для написания игровых "движков", правда, в последнее время на этом по-прище его теснит DirectX.

Перспективы развития

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

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

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

В то же время близится к завершению разработка нового стандарта библиотеки – OpenGL 2.0. Развитие графической аппаратуры вышло за пределы исходной спецификации. Вторая версия OpenGL призвана поднять планку и вновь создать стандарт для компьютерной графики на десятилетия. Помимо прочего OpenGL 2.0 включает в себя возможность программирования всего графического конвейера на языке высокого уровня (подобный язык уже появился в девятой версии DirectX и называется HLSL – High Level Shading Language).