Михаил Краснов - Графика DirectX в Delphi
Примеры к данной главе располагаются в каталоге ExamplesChapter0l, для изучения они должны быть скопированы на диск, допускающий перезапись.
Библиотеки динамической компоновки
Ключевым понятием операционной системы Windows, позволяющим понять любую технологию, использующуюся в ней, является понятие библиотеки динамической компоновки (DLL, Dynamic Link Library). Любое полноценное приложение этой операционной системы (32-разрядное приложение, имеющее собственное окно) использует DLL-файлы. По мере необходимости приложение обращается к библиотекам, вызывая из них нужные функции. Например, выполнимый модуль приложения не содержит кода по отображению окна, вывода в окно и реакции на большинство событий. Перечисленные действия реализуются в системных DLL. В частности, использованием такой технологии удается экономить драгоценные ресурсы, один и тот же код не дублируется многократно, а размещается в памяти единожды.
К одной библиотеке, как правило, может обращаться одновременно несколько приложений. Библиотеку в такой схеме называют сервером, а обслуживаемое им приложение - клиентом. Сервером и клиентом в общем случае могут являться и библиотека, и приложение. В частности, это означает, что некоторая библиотека, в свою очередь, может "подгружать" функции из другой библиотеки.
Продемонстрируем работу операционной системы следующим примером. Создадим библиотеку, содержащую полезную функцию, выводящую на окне вызывающего клиента растровое изображение. Дальше приведем инструкцию ваших действий в среде Delphi. Готовый результат содержится в каталоге ExOl.
В главном меню выберите пункт File | New и в появившемся окне New Items щелкните на значке с подписью "DLL".
Чтобы выводимый растр не оказался легко доступным для посторонних глаз, скроем его, поместив в библиотеку. Для этого с помощью редактора ресурсов Image Editor (для вызова его выберите соответствующую команду меню Tools) создайте новый файл ресурсов с единственным ресурсом - нужным растром. Присвойте имя ресурсу - ВМР1.
Для подготовки этого примера было взято одно из растровых изображений, поставляемых в составе пакета DirectX SDK, скопированное из окна редактора Microsoft Paint через буфер обмена.
Закончив редактировать растр, res-файл запишите в каталог, предназначающийся для проекта библиотеки под именем DLLRes.res.
Код DLL-проекта приведите к следующему виду:
library Projectl; // Проект библиотеки uses
Windows, Graphics;
{$R DLLRes.res} // Подключение файла ресурсов
// Описание экспортируемой функции, размещаемой в DLL (export) и
// вызываемой стандартно (stdcall)
procedure DrawBMP (Handle : THandle); export; stdcall; var
wrkBitmap : TBitmap; wrkCanvas : TCanvas; begin
wrkBitmap := TBitmap.Create; wrkCanvas := TCanvas.Create; try
// Растр загружается из ресурсов, идентифицируется именем wrkBitmap.LoadFromResourceName (HInstance, 'BMP1'); wrkCanvas.Handle := Handle; wrkCanvas.Draw(0, 0, wrkBitmap); finally
wrkCanvas.Free; wrkBitmap.Free;
end;
end;
// Список экспортируемых функций // Функция у нас единственная exports
DrawBMP;
// Следующий блок соответствует инициализации библиотеки begin
end.
Не будет лишним привести некоторые пояснения. Аргументом функции должен являться идентификатор канвы вызываемой формы. У вспомогательного объекта процедуры, класса TCanvas, значение этого идентификатора устанавливается в передаваемое значение, и теперь все его методы будут работать на канве окна, вызывающего функцию приложения.
А сейчас создайте DLL, откомпилировав проект.
Откомпилируйте проект, но не запускайте его. Нельзя запустить DLL в понятии, привычном для обычного приложения.
В каталоге должен появиться файл Projectl.dll. Исследуйте библиотеку: поставьте курсор на ее значок, нажмите правую кнопку мыши и в появившемся контекстном меню выберите команду Быстрый просмотр.
Если данная команда отсутствует, вам необходимо установить соответствующий компонент, входящий в дистрибутив операционной системы.
В окне отобразится информация о содержимом библиотеки, разбитая по секциям, среди которых нас особо интересует секция экспортируемых функций (рис. 1.1).
Если с помощью утилиты быстрого просмотра вы взглянете на содержимое модуля обычного приложения, то не найдете там секции экспортируемых функций. Это принципиальное отличие библиотек динамической компоновки от обычных исполняемых файлов.
Некоторые библиотеки скрывают секцию экспортируемых функций от обычного просмотра, но она там обязательно присутствует, даже если библиотека содержит только ресурсы. Пример подобной библиотеки - системная библиотека moricons.dll.
Итак, созданная нами библиотека содержит код экспортируемой функции с именем DrawBMP и растровое изображение. Сервер готов. Теперь создайте клиента. Организуйте новый проект, сохраните его в другом каталоге (готовый проект содержится в каталоге Ех02).
В секции implementation введите следующую строку:
procedure DrawBMP (Handle : THandle); stdcall; external 'Projectl.dll';
Этим мы декларируем нужную нам функцию. Ключевое слово external указывает, что данная функция размещена в библиотеке с указанным далее именем. Ключевое слово stdcall определяет вызов функции стандартным для операционной системы образом. При использовании импортируемых функций такие параметры задаются обязательно.
На форме разместите кнопку, в процедуре обработки события щелчка кнопки мыши которой введите строку:
DrawBMP (Canvas.Handle);
Аргументом вызываемой функции передаем ссылку канвы окна. Основной смысл этой величины - идентификация полотна окна.
Откомпилируйте проект, но пока не запускайте. С помощью утилиты быстрого просмотра исследуйте содержимое откомпилированного модуля: найдите в списке импортируемых функций следы того, что приложение использует функцию DrawBMP (рис. 1.2).
Обратите внимание, что имя известной нам функции находится в длинном ряду имен других импортируемых приложением функций. То есть модуль даже минимального приложения использует целый сонм функций, подключаемых из DLL. Именно о них упоминалось в начале главы как о функциях, ответственных за появление окна приложения и вывод на канве окна, а также отвечающих за реакцию окна на события. Эти функции именуются системными. Так же называются и библиотеки, хранящие код таких функций.
Другое название системных функций - функции API (Application Program Interface).
При исследовании содержимого созданной нами библиотеки вы могли обратить внимание, что она ко всему прочему импортирует массу функций из системных библиотек.
Delphi позволяет нам писать краткий и удобочитаемый код, но при компиляции этот код преобразуется к вызову массы системных функций, и подчас одна строка кода "расшифровывается" вызовом десятка функций API. Программирование на Delphi образно можно себе представить как общение с операционной системой посредством ловкого переводчика, способного нам одной фразой передать длинную тираду, перевести без потери смысла, но некоторые потери мы все-таки имеем. Прежде всего, мы расплачиваемся тем, что приложения, созданные в Delphi, как правило, имеют сравнительно большой размер. Другая потеря - скорость работы приложения. При использовании библиотеки VCL и концепции объектно-ориентированного программирования вообще, мы жертвуем скоростью работы приложения.
В тех случаях, когда скорость работы приложения чрезвычайно важна, как в случае с обработкой графики, выход может состоять в том, чтобы отказаться от применения "переводчика", писать код, основанный исключительно на использовании функций API. Но такие программы плохо понятны новичкам, требуют специальной подготовки, поэтому мы не будем злоупотреблять этим. Если вы испытаете необходимость подробного разговора о том, как создавать в Delphi приложения без вызова библиотеки классов VCL, то автор может посоветовать вам свою предыдущую книгу, в списке литературы она поставлена на первое место. В ней вы найдете достаточно примеров подобных проектов. Ну а в этой книге постараемся не приводить таких примеров.
Вернемся к нашему примеру. Если вы сейчас запустите приложение, то вас постигнет неудача: сразу после запуска появится системное окно с сообщением о том, что необходимый файл библиотеки не найден. Ничего удивительного, но обратите внимание, что сообщение об ошибке появляется сразу же после запуска приложения, а не после вызова функции, вслед за нажатием кнопки.
Скопируйте в этот же каталог скомпилированную библиотеку и снова запустите приложение. Теперь при запуске все должно быть в порядке, никаких сообщений не появится, а после нажатия кнопки на поверхности окна должна отобразиться картинка (рис. 1.3).
Главное в рассмотренном примере заключается в том, что код приложения не содержит напрямую ничего, связанного с отображаемой в окне картинкой. Приложение обращается к указанной нами библиотеке динамической компоновки, которая выполняет всю работу по выводу изображения.