Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС
выпущен 4 ноября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

БОЛЬШОЙ FAQ ПО DELPHI



Использование и создание DLL в Delphi

У жены программиста спросили:
- А как он за тобой ухаживал?
Жена, после минутного раздумья:
- Ну-у, компьютер показал...

Введение

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

Области применения DLL

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

Отдельные библиотеки
Содержат полезные для программистов дополнительные функции. Например, функции для работы со строками, или же - сложные библиотеки для преобразования изображений.
Хранилища ресурсов
В DLL можно хранить не только программы и функции, но и всевозможные ресурсы - иконки, рисунки, строковые массивы, меню, и т.д.
Библиотеки поддержки
В качестве примера можно привести библиотеки таких известных пакетов, как: DirectX, ICQAPI (API для ICQ), OpenGL и т.д.
Части программы
Например, в DLL можно хранить окна программы (формы), и т.п.
Плагины (Plugins)
Вот где настоящий простор для мыслей программиста! Плагины - дополнения к программе, расширяющие ее возможности. Например, в этой статье мы рассмотрим теорию создания плагина для собственной программы.
Разделяемый ресурс
DLL (Dynamic Link Library) может быть использована сразу несколькими программами или процессами (т.н. sharing - разделяемый ресурс)

Краткое описание функций и приемов для работы с DLL

Итак, какие же приемы и функции необходимо использовать, чтобы работать с DLL? Разберем два метода импортирования функций из библиотеки:

1 способ. Привязка DLL к программе.

Это наиболее простой и легкий метод для использования функций, импортируемых из DLL. Однако (и на это следует обратить внимание) этот способ имеет очень весомый недостаток - если библиотека, которую использует программа, не будет найдена, то программа просто не запустится, выдавая ошибку и сообщая о том, что ресурс DLL не найден. А поиск библиотеки будет вестись: в текущем каталоге, в каталоге программы, в каталоге WINDOWS\SYSTEM, и т.д. Итак, для начала - общая форма этого приема:


 implementation
 ...
 
 function FunctionName(Par1: Par1Type; Par2: Par2Type; ...): ReturnType;
 stdcall; external 'DLLNAME.DLL' name 'FunctionName' index FuncIndex;
 
 // или (если не функция, а процедура):
 
 procedure ProcedureName(Par1: Par1Type; Par2: Par2Type; ...);
 stdcall; external 'DLLNAME.DLL' name 'ProcedureName' index ProcIndex;
 

Здесь:

FunctionName (либо ProcedureName)
имя функции (или процедуры), которое будет использоваться в Вашей программе;
Par1, Par2, ...
имена параметров функции или процедуры;
Par1Type, Par2Type, ...
типы параметров функции или процедуры (например, Integer);
ReturnType
тип возвращаемого значения (только для функции);
stdcall
директива, которая должна точно совпадать с используемой в самой DLL;
external 'DLLNAME.DLL'
директива, указывающая имя внешней DLL, из которой будет импортирована данная функция или процедура (в данном случае - DLLNAME.DLL);
name 'FunctionName' ('ProcedureName')
директива, указывающая точное имя функции в самой DLL. Это необязательная директива, которая позволяет использовать в программе функцию, имеющую название, отличное от истинного (которое она имеет в библиотеке);
index FunctionIndex (ProcedureIndex)
директива, указывающая порядковый номер функции или процедуры в DLL. Это также необязательная директива.

2 способ. Динамическая загрузка DLL

Это гораздо более сложный, но и более элегантный метод. Он лишен недостатка первого метода. Единственное, что неприятно - объем кода, необходимого для осуществления этого приема, причем сложность в том, что функция, импортируемая из DLL достуна лишь тогда, когда эта DLL загружена и находится в памяти... С примером можно ознакомиться ниже, а пока - краткое описание используемых этим методом функций WinAPI:

LoadLibrary(LibFileName: PChar)
загрузка указанной библиотеки LibFileName в память. При успешном завершении функция возвращает дескриптор (THandle) DLL в памяти.
GetProcAddress(Module: THandle; ProcName: PChar)
считывает адpес экспоpтиpованной библиотечной функции. При успешном завершении функция возвращает дескриптор (TFarProc) функции в загруженной DLL.
FreeLibrary(LibModule: THandle)
делает недействительным LibModule и освобождает связанную с ним память. Следует заметить, что после вызова этой процедуры функции данной библиотеки больше недоступны.

Практика и примеры

Ну а теперь пора привести пару примеров использования вышеперечисленных методов и приемов:

Пример 1. Привязка DLL к программе


 {... Здесь идет заголовок файла и определение
 формы TForm1 и ее экземпляра Form1}
 
 implementation
 
 {Определяем внешнюю библиотечную функцию}
 
 function GetSimpleText(LangRus: Boolean): PChar;
 stdcall; external 'MYDLL.DLL';
 
 procedure Button1Click(Sender: TObject);
 begin
   {И используем ее}
   ShowMessage(StrPas(GetSimpleText(True)));
   ShowMessage(StrPas(GetSimpleText(False)));
   {ShowMessage - показывает диалоговое окно с указанной надписью;
   StrPas - преобразует строку PChar в string}
 end;
 

Теперь то же самое, но вторым способом - с динамической загрузкой:

Пример 2. Динамическая загрузка DLL


 {... Здесь идет заголовок файла и определение
 формы TForm1 и ее экземпляра Form1}
 
 var
   Form1: TForm1;
   GetSimpleText: function(LangRus: Boolean): PChar;
   LibHandle: THandle;
 
 procedure Button1Click(Sender: TObject);
 begin
   {"Чистим" адрес функции от "грязи"}
   @GetSimpleText := nil;
   {Пытаемся загрузить библиотеку}
   LibHandle := LoadLibrary('MYDLL.DLL');
   {Если все OK}
   if LibHandle >= 32 then
   begin
     {...то пытаемся получить адрес функции в библиотеке}
     @GetSimpleText := GetProcAddress(LibHandle,'GetSimpleText');
     {Если и здесь все OK}
     if @GetSimpleText <> nil then
       {...то вызываем эту функцию и показываем результат}
       ShowMessage(StrPas(GetSimpleText(True)));
   end;
   {И не забываем освободить память и выгрузить DLL}
   FreeLibrary(LibHandle);
 end;
 

Примечание:

Следует воздерживаться от использования типа string в библиотечных функциях, т.к. при его использовании существуют проблемы с "разделением памяти". Подробней об этом можно прочитать (правда, на английском) в тексте пустого проекта DLL, который создает Delphi (File -> New -> DLL). Так что лучше используйте PChar, а затем при необходимости конвертируйте его в string функцией StrPas.

Ну а теперь разберем непосредственно саму библиотеку DLL:

Пример 3. Исходник проекта MYDLL.DPR


 library mydll;
 
 uses
   SysUtils, Classes;
 
 {Определяем функцию как stdcall}
 function GetSimpleText(LangRus: Boolean): PChar; stdcall;
 begin
   {В зависимости от LangRus возвращаем русскую (True) либо английскую (False) фразу}
   if LangRus then
     Result := PChar('Здравствуй, мир!')
   else
     Result := PChar('Hello, world!');
 end;
 
 {Директива exports указывает, какие функции будут экспортированы этой DLL}
 exports GetSimpleText;
 
 begin
 end.
 

Размещение в DLL ресурсов и форм

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

Для этого нужно создать новую DLL и добавить в нее новую форму (File -> New -> DLL, а затем - File -> New Form). Далее, если форма представляет собой диалоговое окно (модальную форму (bsDialog)), то добавляем в DLL следующую функцию (допустим, форма называется Form1, а ее класс - TForm1):

Пример 4. Размещение формы в DLL


 function ShowMyDialog(Msg: PChar): Boolean; stdcall;
 
 ...
 exports ShowMyDialog;
 
 function ShowMyDialog(Msg: PChar): Boolean;
 begin
   {Создаем экземпляр Form1 формы TForm1}
   Form1 := TForm1.Create(Application);
   {В Label1 выводим Msg}
   Form1.Label1.Caption := StrPas(Msg);
   {Возвращаем True только если нажата OK (ModalResult = mrOk)}
   Result := (Form1.ShowModal = mrOk);
   {Освобождаем память}
   Form1.Free;
 end;
 

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

Создание плагинов

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

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

Эпилог

В этой статье отображены основные стороны использования и создания библиотек DLL в Borland Delphi. Если у Вас есть вопросы - скидывайте их мне на E-mail: snick@mailru.com




<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ



Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS