Создание DLL-библиотеки на 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, и т.д. Итак, для начала - общая форма этого приема:
FunctionName (либо ProcedureName) имя функции (или процедуры), которое будет использоваться в Вашей программе; Par1, Par2, ... имена параметров функции или процедуры; Par1Type, Par2Type, ... типы параметров функции или процедуры (например, Integer); ReturnType тип возвращаемого значения (только для функции); stdcall директива, которая должна точно совпадать с используемой в самой DLL; external "DLLNAME.DLL" директива, указывающая имя внешней DLL, из которой будет импортирована данная функция или процедура (в данном случае - DLLNAME.DLL); name "FunctionName" ("ProcedureName") директива, указывающая точное имя функции в самой DLL. Это необязательная директива, которая позволяет использовать в программе функцию, имеющую название, отличное от истинного (которое она имеет в библиотеке); index FunctionIndex (ProcedureIndex) директива, указывающая порядковый номер функции или процедуры в 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 к программе
Теперь то же самое, но вторым способом - с динамической загрузкой:
Примечание:
Следует воздерживаться от использования типа string в библиотечных функциях, т.к. при его использовании существуют проблемы с "разделением памяти". Подробней об этом можно прочитать (правда, на английском) в тексте пустого проекта DLL, который создает Delphi (File -> New -> DLL). Так что лучше используйте PChar, а затем при необходимости конвертируйте его в string функцией StrPas.
Ну а теперь разберем непосредственно саму библиотеку DLL:
Пример 3. Исходник проекта MYDLL.DPR
Размещение в DLL ресурсов и форм
В DLL можно размещать не только функции, но и курсоры, рисунки, иконки, меню, текстовые строки. На этом мы останавливаться не будем. Замечу лишь, что для загрузки ресурса нужно загрузить DLL, а затем, получив ее дескриптор, - загружать сам ресурс соотвествующей функцией (LoadIcon, LoadCursor, и т.д.). В этом разделе мы лишь немного затронем размещение в библиотеках DLL окон приложения (т.е. форм в Дельфи).
Для этого нужно создать новую DLL и добавить в нее новую форму (File -> New -> DLL, а затем - File -> New Form). Далее, если форма представляет собой диалоговое окно (модальную форму (bsDialog)), то добавляем в DLL следующую функцию (допустим, форма называется Form1, а ее класс - TForm1):
Пример 4. Размещение формы в DLL
Если же нужно разместить в DLL немодальную форму, то необходимо сделать две функции - открытия и закрытия формы. При этом нужно заставить DLL запомнить дескриптор этой формы.
Создание плагинов
Здесь мы не будем подробно рассматривать плагины, т.к. уже приведенные выше примеры помогут Вам легко разобраться в львиной части программирования DLL. Напомню лишь, что плагин - дополнение к программе, расширяющее ее возможности. При этом сама программа обязательно должна предусматривать наличие таких дополнений и позволять им выполнять свое предназначение.
Т.е., например, чтобы создать плагин к графическому редактору, который бы выполнял преобразование изображений, Вам нужно предусмотреть как минимум две функции в плагине (и, соответственно, вызвать эти функции в программе) - функция, которая бы возвращала имя плагина (и/или его тип), чтобы добавить этот плагин в меню (или в тулбар), плюс главная функция - передачи и приема изображения. Т.е. сначала программа ищет плагины, потом для каждого найденного вызывает его опозновательную функцию со строго определенным именем (например, GetPluginName) и добавляет нужный пункт в меню, затем, если пользователь выбрал этот пункт - вызывает вторую функцию, которой передает входное изображение (либо имя файла, содержащего это изображение), а эта функция, в свою очередь, обрабатывает изображение и возвращает его в новом виде (или имя файла с новым изображением). Вот и вся сущность плагина... :-)
Эпилог
В этой статье отображены основные стороны использования и создания библиотек DLL в Borland Delphi. Если у Вас есть вопросы - скидывайте их мне на E-mail:
Здравствуйте уважаемые коллеги!
В данной статье я постараюсь ответить на вопросы: Что такое DLL ? Для чего она нужна? И как создавать и использовать DLL при помощи Delphi .
Что такое DLL ?
Dynamic Link Library или сокращенно DLL — это библиотека, которая содержит в себе набор данных или функций для использования в других программах.
Области применения:
- Хранение ресурсов: иконки, звуки, курсоры и т.д. Экономим на размере исполняемых файлов объединяя ресурсы в единую библиотеку.
- Размещение отдельных модулей программы и форм интерфейса. Получаем возможность частичного обновления приложения, а при динамическом подключении, возможно обновлять модули без перезапуска основной программы.
- Использование в качестве плагинов (PlugIn). Предоставляем возможность расширять функционал приложения без переписывания основного кода программы.
- Библиотека может использоваться на разных языках программирования в не зависимости от языка на котором она была написана.
Создание собственной библиотеки DLL.
Для создания библиотеки заходим в меню File
-> Other
и выбираем Delphi Projects
-> Dynamic-link Library
.
Откроется текст кода с заготовкой для создания библиотеки:
Library Project1; uses System.SysUtils, System.Classes; {$R *.res} begin end.
Размещение ресурсов
Добавим иконку в библиотеку, которую будем использовать потом в основной программе.
Как добавлять ресурсы в проект подробно описано в статье.
Добавим форму «О программе».
Нажимаем File
-> New
-> VCL Form
. Добавим текст и кнопку «ОК»:
Добавим функцию, которая будет выводить стандартный MessageBox с вопросом, кнопками «Да», «Нет» и с результатом в виде True или False .
Function YesNoDlg(const Question: PChar): boolean; stdcall; begin Result:= (MessageBox(0, Question, "Подтверждение", MB_YESNO + MB_ICONQUESTION) = ID_YES); end;
Обратите внимание на ключевое слово stdcall . Оно определяет как именно будут передаваться параметры и результаты при вызове функций. Более подробно можно почитать на странице wiki Соглашение о вызове.
Так же добавим процедуру, которая будет использоваться как плагин для основной программы. Которая будет добавлять кнопку «О программе» в основное меню программы для вызова созданного нами окна.
Код процедуры:
Procedure PlugIn(Form: TForm); stdcall; var i: integer; mi: TMenuItem; begin for i:= 0 to Form.ComponentCount - 1 do begin if (Form.Components[i].ClassName = "TMenuItem") and (Form.Components[i].Name = "miHelp") then begin mi:= TMenuItem.Create(Form.Components[i]); mi.Caption:= "О программе"; mi.OnClick:= fmAbout.onAboutButtonClick; TMenuItem(Form.Components[i]).Add(mi); Exit; end; end; end;
В процедуру в качестве параметра передается форма программы. Мы проверяем присутствует ли на ней пункт меню с именем «miHelp » и если меню нашлось, то добавляем в него свою кнопку.
Теперь укажем какие функции и процедуры можно использовать из нашей библиотеки.
Добавим строку:
Exports PlugIn, YesNoDlg;
Скомпилируем функцию Project
-> Build
или с помощью горячей клавиши Shift+F9
.
Если ошибок в коде нет, то в папке проекта должен появиться файл с расширением DLL
.
Теперь перейдем к созданию приложения, которое будет использовать нашу библиотеку.
В приложении создадим форму Main в которой добавим компонент со следующими кнопками: Программа -> Выход и Помощь. Для последнего зададим имя — miHelp :
Перейдем к коду формы.
Функции из библиотеки DLL
можно подключать двумя способами:
Статическим
— библиотека подключается при запуске программы. Если библиотека или имя функции не найдено, то программа выдаст ошибку и не запустится.
Динамическим
— библиотека подключается или непосредственно перед вызовом функции или по определенному событию.
Рассмотрим статический способ подключения:
Type TfmMain = class(TForm) MainMenu: TMainMenu; miProgram: TMenuItem; miExit: TMenuItem; miHelp: TMenuItem; procedure FormCreate(Sender: TObject); procedure miExitClick(Sender: TObject); private public end; procedure PlugIn(Form: TForm); stdcall; external "SampleDLL.dll"; var ...
Ключевое слово external указывает на то, что данная функция подключается из внешней библиотеки.
На событие onCreate формы добавим вызов процедуры PlugIn :
Procedure TfmMain.FormCreate(Sender: TObject); begin PlugIn(Self); end;
В качестве параметра Form передаем в процедуру текущую форму (ключевое слово Self ).
При запуске программы у нас должен появится пункт «О программе» в разделе «Помощь» главного меню.
Переходим к динамическому способу подключения.
Нам понадобятся три функции WinApi :
LoadLibrary
Загружает библиотеку в память компьютера. В качестве результата возвращает указатель на библиотеку в памяти. В случае ошибки вернет 0.
LoadLibrary(lpLibFileName: LPCWSTR): HMODULE;
lpLibFileName — Имя файла библиотеки.
GetProcAddress
Найти функцию в библиотеки по имени. Результатом будет указатель на функцию. Если функция не найдена, то вернет nil
.
GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC;
hModule
lpProcName
— Имя функции.
FreeLibrary
Выгружает библиотеку из памяти компьютера. Результатом будет True в случае успешного выполнения и False в случае ошибки.
FreeLibrary(hLibModule: HMODULE): BOOL;
hLibModule — Указатель на загруженную библиотеку.
Теперь с помощью динамического способа получим ресурсы, нашу иконку и на кнопку «Выход» добавим вызов функции YesNoDlg
для подтверждения закрытия программы.
На то же событие onCreate добавляем код:
Procedure TfmMain.FormCreate(Sender: TObject); var DLLHandle: THandle; begin PlugIn(Self); DLLHandle:= LoadLibrary("SampleDLL.dll"); if DLLHandle = 0 then raise Exception.Create("Не удалось подключить библиотеку "SampleDLL"!"); try Self.Icon.LoadFromResourceName(DLLHandle, "my_icon"); finally FreeLibrary(DLLHandle); end; end;
И на событие onClick пункта меню «Выход»:
Procedure TfmMain.miExitClick(Sender: TObject); var DLLHandle: THandle; Dlg: function(const Question: PChar): boolean; stdcall; begin DLLHandle:= LoadLibrary("SampleDLL.dll"); if DLLHandle = 0 then raise Exception.Create("Не удалось подключить библиотеку "SampleDLL"!"); try @Dlg:= GetProcAddress(DLLHandle, "YesNoDlg"); if not Assigned(@Dlg) then raise Exception.Create("Функция с именем "YesNoDlg" не найдена в библиотеке "SampleDLL"!"); if Dlg("Выйти из программы?") then Close; finally FreeLibrary(DLLHandle); end; end;
Если вы все написали верно, то после запуска программы должна поменяться иконка формы, добавиться кнопка «О программе», при нажатии на которую будет показывать форма About и на нажатие кнопки выход программа будет запрашивать подтверждение: «Выйти из программы?».
Надеюсь вам будет полезен этот небольшой пример использования возможностей DLL
библиотек.
Исходники проекта можно скачать .
В связи с бурным развитием технологий программирования, все больше людей сталкиваются с проблемой наращивания возможностей своих программ. Данная статья посвящена именно этому вопросу, а именно - программирование 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 и освобождает связанную с ним память. Следует заметить, что после
вызова этой процедуры функции данной библиотеки больше недоступны.
Практика и примеры
Ну а теперь пора привести пару примеров использования вышеперечисленных методов и приемов:
Теперь то же самое, но вторым способом - с динамической загрузкой:
|
{... Здесь идет заголовок файла и
определение формы TForm1 и ее экземпляра
Form1}
var
procedure Button1Click(Sender:
TObject); |
ПРИМЕЧАНИЕ : Следует воздерживаться от использования типа string в библиотечных функциях, т.к. при его использовании существуют проблемы с "разделением памяти". Подробней об этом можно прочитать (правда, на английском) в тексте пустого проекта DLL, который создает Delphi (File -> New -> DLL). Так что лучше используйте PChar, а затем при необходимости конвертируйте его в string функцией StrPas.
Ну а теперь разберем непосредственно саму библиотеку DLL:
Размещение в DLL ресурсов и форм
В DLL можно размещать не только функции, но и курсоры, рисунки, иконки, меню, текстовые строки. На этом мы останавливаться не будем. Замечу лишь, что для загрузки ресурса нужно загрузить DLL, а затем, получив ее дескриптор, - загружать сам ресурс соотвествующей функцией (LoadIcon, LoadCursor, и т.д.). В этом разделе мы лишь немного затронем размещение в библиотеках DLL окон приложения (т.е. форм в Дельфи).
Для этого нужно создать новую DLL и добавить в нее новую форму (File -> New -> DLL, а затем - File -> New Form). Далее, если форма представляет собой диалоговое окно (модальную форму (bsDialog)), то добавляем в DLL следующую функцию (допустим, форма называется Form1, а ее класс - TForm1):
Если же нужно разместить в DLL немодальную форму, то необходимо сделать две функции - открытия и закрытия формы. При этом нужно заставить DLL запомнить дескриптор этой формы.
Создание плагинов
Здесь мы не будем подробно рассматривать плагины, т.к. уже приведенные выше примеры помогут Вам легко разобраться в львиной части программирования DLL. Напомню лишь, что плагин - дополнение к программе, расширяющее ее возможности. При этом сама программа обязательно должна предусматривать наличие таких дополнений и позволять им выполнять свое предназначение.
Т.е., например, чтобы создать плагин к графическому редактору, который бы выполнял преобразование изображений, Вам нужно предусмотреть как минимум две функции в плагине (и, соответственно, вызвать эти функции в программе) - функция, которая бы возвращала имя плагина (и/или его тип), чтобы добавить этот плагин в меню (или в тулбар), плюс главная функция - передачи и приема изображения. Т.е. сначала программа ищет плагины, потом для каждого найденного вызывает его опозновательную функцию со строго определенным именем (например, GetPluginName) и добавляет нужный пункт в меню, затем, если пользователь выбрал этот пункт - вызывает вторую функцию, которой передает входное изображение (либо имя файла, содержащего это изображение), а эта функция, в свою очередь, обрабатывает изображение и возвращает его в новом виде (или имя файла с новым изображением). Вот и вся сущность плагина... :-)
Эпилог
В этой статье отображены основные стороны использования и создания библиотек DLL в Borland Delphi. Если у Вас есть вопросы - скидывайте их мне на E-mail: [email protected] , а еще лучше - пишите в конференции этого сайта, чтобы и другие пользователи смогли увидеть Ваш вопрос и попытаться на него ответить!
Карих Николай. Московская область, г.Жуковский
DLL-библиотека позволяет объединить в одно целое повторно используемый код. Функции из DLL - библиотеки могут подключаться динамически во время выполнения, в отличие от функций из пакетов Delphi, линкуемых статически на этапе компиляции приложения.
Для того чтобы создать DLL-библиотеку, для начало необходимо выполнить команду меню File|New|Other и выбрать на странице New диалога New Item элемент DLL Wizard.
Мастер DLL Wizard автоматически создаст пустой шаблон для DLL-библиотеки. В отличие от обычного модуля, начинающегося с ключевого слова unit, модуль DLL-библиотеки начинается с ключевого слова library. Секция uses модуля DLL-библиотеки требует подключения только двух пакетов: SysUtils и Classes.
Создание DLL-функции состоит из нескольких этапов:
1. Сначала в секции реализации модуля следует ввести сигнатуру функции и запрограммировать код, выполняемый функцией.
3. В заключение функцию, которую предполагается использовать не только внутри модуля, но и вызывать из других приложений, следует объявить как экспортируемую в секции exports.
Функции из DLL-библиотеки могут вызываться как из приложений, разработанных в Delphi, так и из приложений, написанных на других языках программирования, таких, как C++.
Порядок выделения памяти под параметры и освобождения ее различен для разных языков программирования. Для того чтобы не возникла ошибка времени выполнения, объявление функции в DLL-библиотеке и ее объявление в приложении должны использовать одинаковый механизм передачи параметров. При объявлении процедуры или функции может быть указан один из следующих механизмов передачи параметров:
Способ передачи параметров указывается через точку с запятой после описания функции. Например:
function F1 (X, Y, Z: Real]: Real; stdcall;.
Различные способы передачи параметров определяют порядок передачи параметров (слева направо или справа налево), а также указывают, кто будет освобождать память стека (вызываемая или вызывающая процедура). При использовании DLL-библиотек в качестве компонентов, вызываемых из приложений на других языках программирования, следует использовать соответствующий модификатор вызова. Для приложений на C++ применяется модификатор вызова stdcall.
Для того чтобы функцию, описанную в DLL-библиотеке, можно было вызвать из другого приложения, эту функцию следует экспортировать. Список всех экспортируемых функций указывается в секции exports через запятую
и завершается символом точка с запятой. Экспорт функций может выполняться тремя способами:
По имени функции, используемому в DLL-библиотеке;
По имени функции, заданному как имя экспорта;
По присвоенному функции индексу.
Для того чтобы присвоить функции некоторый индекс, его следует указать в секции exports после имени функции с ключевым словом index.
Для того чтобы экспортируемая функция вызывалась по имени, отличном от имени, используемого в DLL-библиотеке, в секции exports после имени функции следует указать ключевое слово name и новое имя экспорта для данной функции.
DLL - библиотека не является выполняемым модулем. Для получения ее кода достаточно произвести компиляцию проекта.
library Projectl;
SysUtils, Classes;
function F1(X, Y: Integer): Integer; stdcall;
Статическое подключение DLL-библиотеки
DLL-библиотека может подключаться или статически, или динамически. При подключении DLL-библиотеки она загружается в память приложения.
При статическом подключении DLL-библиотека загружается один раз при запуске приложения. На всем протяжении выполнения приложения имя функции, импортируемой из DLL-библиотеки, которая была подключена статически, указывает на одну и ту же функцию (точку входа в DLL) в одной и той же DLL. Все функции из DLL-библиотеки, которые будут использоваться в приложении первоначально, должны быть объявлены как внешние. При этом следует указать, если требуется, модификатор вызова. Если функция вызывается по индексу, то для нее следует задать имя, используемое в приложении, и индекс функции в DLL-библиотеке.
Объявления внешних функций выполняется в секции implementation до использования этих функций.
Объявление внешней функции с ключевым словом external определяет, что будет использовано статическое связывание.
TForml = class(TForm)
Editl: TEdit; [Поле для ввода первого значения}
Edit2: TEdit; (Поле для ввода второго значения}
Edit3: TEdit; (Поле для отображения результата
выполнения функции из DLL-библиотеки}
Buttonl: TButton; {Выполняется вызов функции, используемой по имени)
Button2: TButton; [Выполняется вызов функции, используемой по индексу}
procedure ButtonlClickfSender: TObject);
procedure Button2Click(Sender: TObject);
{ Private declarations }
(Public declarations }
(Объявление экспортируемых функций}
function Fl (i: Integer; j:Integer): Integer; stdcall;
external "Projectl.dll";
function F2 (i: Integer; j:Integer): Integer; stdcall;
external "Projectl.dll index 2;
procedure TForml.ButtonlClick(Sender: TObject);
{Вызов экспортируемой функции}
Edit3.Text:=IntToStr(Fl(StrToInt(Editl.Text),StrToInt{Edit2.Text)));
procedure TForml.Button2Click(Sender: TObject);
Edit3.Text:=JntToStr(F2(StrToInt(Editl.Text),StrToInt(Edit2.Text)));
Динамическое подключение DLL-библиотеки
В отличие от статического подключения DLL-библиотеки, выполняемого в момент загрузки приложения, динамическое подключение DLL-библиотеки может быть выполнено в любой точке выполнения программы. После вызова функции из DLL-библиотеки ее можно отключить. При одновременном использовании нескольких DLL-библиотек это дает ощутимую экономию памяти. Для динамического подключения DLL-библиотеки используются функции Windows API. Windows API - это набор стандартных функций, используемый для реализации взаимодействия с операционной системой.
При вызове функции из динамически подключаемой DLL-библиотеки вместо определения имени функции как external в случае статического связывания следует определить новый тип, соответствующий типу вызываемой функции, и создать переменную данного типа.
Для того чтобы выполнить вызов функции из динамически подключаемой DLL-библиотеки, выполните следующие действия:
1. Создайте новый тип. соответствующий типу вызываемой функции (имя нового типа можно ввести после секции type).
Например:
TMyFl=function(i,j:Integer):Integer; stdcall;
2. В секции var interface-секции модуля создайте переменную созданного типа функции. Например: MyFl: TMyFl;
3. Перед загрузкой DLL-библиотеки объявите переменную типа Integer, которая будет содержать дескриптор подключаемой библиотеки.
4. Вызовите метод LoadLibrary, выполняющий подключение DLL-библиотеки. Например; h:=LoadLibrary ("Projectl.dll");
5. Проверьте, успешно ли выполнено подключение библиотеки. Если имя DLL-библиотеки указано неверно или библиотека не найдена, то функция LoadLibrary вернет значение 0.
6. В случае успешного подключения DLL-библиотеки далее следует получить адрес функции. Для этого используется функция Windows API GetProcAddress, в качестве параметров которой указывается дескриптор DLL-библиотеки и имя подключаемой функции. Например: @MyFl: =GetProcAddress (h, " Fl ");
7. Если адрес функции получен, то значение адреса (в нашем примере @MyFl) не должно быть равно nil.
8. На этом этапе можно выполнять вызов функции из динамически подключенной DLL-библиотеки.
9. Для освобождения и соответственно выгрузки DLL-библиотеки вызовите метод FreeLibrary, выполняющий отключение DLL-библиотеки.
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
TForml = class(TForm)
Button3: TButton;
procedure Button3Click
procedure TForml.Button3Click(Sender: TObject);
h:=LoadLibrary("Projectl.dll");
if h <> 0 then
@MyFl:=GetProcAddress(h,"Fl");
if @MyFl <> nil then
Edit3.Text:=IntToStr(MyFl(StrToInt{Editl.Text),
StrToInt(Edit2.Text)));
Использование DLL-библиотеки для вызова общих модальных диалогов.
Результатом выполнения процедуры из DLL-библиотеки может быть отображение некоторого модального диалога. Для этого следует в экспортируемом методе создать объект форма, отобразить ее как модальный диалог, а затем удалить объект форма. При этом в самой форме следует предусмотреть вызов метода Close для завершения диалога.
Для создания формы используется метод Create, в качестве параметра которому передается указатель на родительскую форму - форму вызывающего приложения. Этот параметр передается вызываемой DLL-функции.
library Projectl;
Unitl_DLL in "Unitl_DLL.pas" {Forml};
procedure MyModalForm (var Z:Integer ;F:TForm1); stdcall;
Form1:=TForml.Create(F);
(Параметр F передается при вызове процедуры и содержит указатель
на родительскую форму - форму вызывающего приложения}
В среде программирования Delphi предусмотрены встроенные средства для быстрого создания DLL-библиотек.
Давайте для определенности создадим библиотеку, содержащую функцию
GetArea(a, b, c: REAL):REAL.
Данной функции на вход подаются длины сторон треугольника. Функция возвращает площадь заданного треугольника:
p:=(a+b+c)/2;
Result:=SQRT(p*(p-a)*(p-b)*(p-c))
Запускаем D elphi , а дальше действуем нетрадиционно. Выбираем пункты меню F ile N ew O ther , в открывшемся окошке на закладке N ew щелкаем по пиктограмме DLL W izard . (алгоритм зависит от версии)
При этом создается файл заготовки DLL-библиотеки. Он очень похож на обычный модуль (unit ) D elphi , только начинается с оператора Library . Сохраните проект под именем, которое в будущем получит DLL-библиотека, скажем, GetA . Название GetArea использовать нельзя – оно уже занято именем функции.
Теперь после оператора USES пишем текст нашей функции, но с некоторыми изменениями в заголовке:
FUNCTION GetArea(a, b, c:REAL):REAL; export;
Ключевое слово EXPORT указывает на то, что данная функция является экспортируемой и будет видна из внешних программ.
После текста функции припишем
GetArea ;
В операторе EXPORTS перечисляются все процедуры и функции, экспортируемые из библиотеки. Это своего рода каталог нашей библиотеки.
Запустить на выполнение библиотеку невозможно, ее можно только откомпилировать. Для этого выполним пункт меню Project → Build. Если все было сделано правильно, на диске в текущей директории будет создан файл с именем geta.dll. Это и есть наша библиотека.
Важное замечание : существует определенная тонкость при передаче процедурам и функциям, находящимся в библиотеке, параметров типа STRING.
Для того, чтобы можно было передавать параметры типа STRING, придется в операторы USES и библиотеки, и вызывающей ее программы прописать подключение модуля ShareMem, да еще и обязательно так, чтобы этот модуль шел первым в списке. Мало того, вместе с библиотекой придется обязательно прилагать файл borlndmm.dll (он входит в поставку Delphi). Избежать этой ситуации просто: следует для параметров текстового типа использовать типы данных ShortString (это обычная строка, но длиной до 255 символов) и PChar (указатель на текстовую строку).
Вызов dll
Существуют два способа вызова процедур и функций из DLL. В первом случае мы заранее, на этапе разработки программы, знаем, какую DLL будем к ней подключать (чаще всего мы сами и создаем эту DLL). Во втором случае мы подключаемся к произвольной библиотеке, в том числе и "чужой".
Статическое связывание
Для реализации первого способа, называемого статическим связыванием , создайте новое обычное приложение, поместите на форму три поля ввода LabeledEdit1…LabeledEdit3, кнопку и компонент Tlabel. После оператора IMPLEMENTATION добавьте строчку, обеспечивающую импорт функции GetArea из библиотеки geta.dll:
Function GetArea(a,b,c:real):REAL; FAR; EXTERNAL "geta";
Слово EXTERNAL указывает на то, что тело данной функции находится в библиотеке с указанным именем, а слово FAR задает применение "длинных" четырехбайтных адресов, что необходимо, так как вызывающая программа находится на одной странице памяти, а DLL-библиотека – на другой. Разумеется, файл geta.dll надо поместить в ту же директорию, где находятся все файлы текущего приложения.
В обработчике нажатия кнопки надо создать массив и передать его библиотечный функции, а результат вывести на экран:
procedure TForm1.Button1Click(Sender: TObject);