Создание и использование DLL в Delphi (привязка DLL к программе, динамическая загрузка).
DLL-библиотека позволяет объединить в одно целое повторно используемый код. Функции из DLL - библиотеки могут подключаться динамически во время выполнения, в отличие от функций из пакетов Delphi, линкуемых статически на этапе компиляции приложения.
Для того чтобы создать DLL-библиотеку, для начало необходимо выполнить команду меню File|New|Other и выбрать на странице New диалога New Item элемент DLL Wizard.
Мастер DLL Wizard автоматически создаст пустой шаблон для DLL-библиотеки. В отличие от обычного модуля, начинающегося с ключевого слова unit, модуль DLL-библиотеки начинается с ключевого слова library. Секция uses модуля DLL-библиотеки требует подключения только двух пакетов: SysUtils и Classes.
Создание DLL-функции состоит из нескольких этапов:
1. Сначала в секции реализации модуля следует ввести сигнатуру функции и запрограммировать код, выполняемый функцией.
2. Далее в сигнатуре функции надо определить, каким образом будет происходить вызов параметров.
3. В заключение функцию, которую предполагается использовать не только внутри модуля, но и вызывать из других приложений, следует объявить как экспортируемую в секции exports.
Функции из DLL-библиотеки могут вызываться как из приложений, разработанных в Delphi, так и из приложений, написанных на других языках программирования, таких, как C++.
Порядок выделения памяти под параметры и освобождения ее различен для разных языков программирования. Для того чтобы не возникла ошибка времени выполнения, объявление функции в DLL-библиотеке и ее объявление в приложении должны использовать одинаковый механизм передачи параметров. При объявлении процедуры или функции может быть указан один из следующих механизмов передачи параметров:
• register,
• pascal,
• cdecl,
• stdcall,
• safecall.
Способ передачи параметров указывается через точку с запятой после описания функции. Например:
function F1 (X, Y, Z: Real]: Real; stdcall;.
Различные способы передачи параметров определяют порядок передачи параметров (слева направо или справа налево), а также указывают, кто будет освобождать память стека (вызываемая или вызывающая процедура). При использовании DLL-библиотек в качестве компонентов, вызываемых из приложений на других языках программирования, следует использовать соответствующий модификатор вызова. Для приложений на C++ применяется модификатор вызова stdcall.
Для того чтобы функцию, описанную в DLL-библиотеке, можно было вызвать из другого приложения, эту функцию следует экспортировать. Список всех экспортируемых функций указывается в секции exports через запятую
и завершается символом точка с запятой. Экспорт функций может выполняться тремя способами:
• по имени функции, используемому в DLL-библиотеке;
• по имени функции, заданному как имя экспорта;
• по присвоенному функции индексу.
Для того чтобы присвоить функции некоторый индекс, его следует указать в секции exports после имени функции с ключевым словом index.
Для того чтобы экспортируемая функция вызывалась по имени, отличном от имени, используемого в DLL-библиотеке, в секции exports после имени функции следует указать ключевое слово name и новое имя экспорта для данной функции.
DLL - библиотека не является выполняемым модулем. Для получения ее кода достаточно произвести компиляцию проекта.
Пример:
library Projectl;
uses
SysUtils, Classes;
{$R *.res}
function F1(X, Y: Integer): Integer; stdcall;
begin
F1:=X+Y;
end;
Статическое подключение DLL-библиотеки
DLL-библиотека может подключаться или статически, или динамически. При подключении DLL-библиотеки она загружается в память приложения.
При статическом подключении DLL-библиотека загружается один раз при запуске приложения. На всем протяжении выполнения приложения имя функции, импортируемой из DLL-библиотеки, которая была подключена статически, указывает на одну и ту же функцию (точку входа в DLL) в одной и той же DLL. Все функции из DLL-библиотеки, которые будут использоваться в приложении первоначально, должны быть объявлены как внешние. При этом следует указать, если требуется, модификатор вызова. Если функция вызывается по индексу, то для нее следует задать имя, используемое в приложении, и индекс функции в DLL-библиотеке.
Объявления внешних функций выполняется в секции implementation до использования этих функций.
Объявление внешней функциис ключевым словом external определяет, что будет использовано статическое связывание.
"
Пример:
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
Type
TForml = class(TForm)
Editl: TEdit; [Поле для ввода первого значения}
Edit2: TEdit; (Поле для ввода второго значения}
Edit3: TEdit; (Поле для отображения результата
выполнения функции из DLL-библиотеки}
Buttonl: TButton; {Выполняется вызов функции, используемой по имени)
Button2: TButton; [Выполняется вызов функции, используемой по индексу}
procedure ButtonlClickfSender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
( Public declarations }
end;
var
Forml: TForml;
implementation
{$R *.dfm)
(Объявление экспортируемых функций}
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);
{Вызов экспортируемой функции}
begin
Edit3.Text:=IntToStr(Fl(StrToInt(Editl.Text),StrToInt{Edit2.Text)));
end;
procedure TForml.Button2Click(Sender: TObject);
begin
Edit3.Text:=JntToStr(F2(StrToInt(Editl.Text),StrToInt(Edit2.Text)));
end;
end.
Динамическое подключение 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-библиотеки.
Пример:
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForml = class(TForm)
iditl: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Button3: TButton;
procedure Button3Click[Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TMyFl=function( i,j:Integer):Integer; stdcall; {Создание типа
функции)
var
Forml: TForml;
MyFl : TMyFl; (Объявление переменной типа функции]
implementation
{$R *.dfm}
procedure TForml.Button3Click(Sender: TObject);
var
h: Integer;
begin
h:=LoadLibrary('Projectl.dll');
if h <> 0 then
begin
@MyFl:=GetProcAddress(h,'Fl');
if @MyFl <> nil then
Edit3.Text:=IntToStr(MyFl(StrToInt{Editl.Text),
StrToInt(Edit2.Text)));
FreeLibrary(h);
end;
end;
end.
Использование DLL-библиотеки для вызова общих модальных диалогов.
Результатом выполнения процедуры из DLL-библиотеки может быть отображение некоторого модального диалога. Для этого следует в экспортируемом методе создать объект форма, отобразить ее как модальный диалог, а затем удалить объект форма. При этом в самой форме следует предусмотреть вызов метода Close для завершения диалога.
Для создания формы используется метод Create, в качестве параметра которому передается указатель на родительскую форму - форму вызывающего приложения. Этот параметр передается вызываемой DLL-функции.
Далее приведен пример DLL-библиотеки, содержащей функцию, используемую для отображения модальной формы и возврата введенного в нее значения.
Пример:
library Projectl;
uses
SysUtils,
Classes,
Unitl_DLL in 'Unitl_DLL.pas' {Forml};
{$R '.res}
procedure MyModalForm (var Z:Integer ;F :TForm1); stdcall;
begin
Form1:=TForml.Create(F);
(Параметр F передается при вызове процедуры и содержит указатель
на родительскую форму - форму вызывающего приложения}
Form1.ShowModal();
(Первый параметр используется для возвращаемого значения}
Z:=StrToInt(Form1.Edit1.Text);
Form1.Free;
end;
exports MyModalForm;
end.
Для того чтобы использовать данную DLL-библиотеку с целью вызова созданного модального диалога, можно применять как динамическое, так и статическое связывание. Следующий пример иллюстрирует вызов функции из
DLL-библиотеки, отображающей модальный диалог.
Пример:
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms,
Dialogs, StdCtris;
type
TForml = class(TForml
Button4: TButton;
Edit4: TEdit;
procedure Button4Click(Sender: TObject);
private
public
end;
TMyModalFora=procedure(var Z:Integer ; F :TForml); stdcall;
var
Forml: TForml;
MyModalForm : TMyHodalForm ; (Переменная типа
вызываемой процедуры]
implementation
{$R *.dfml
procedure TForml.Button4Click(Sender: TObject);
var
244 Глава 6
h: Integer;
ii: Integer; {Для сохранения возвращаемого значения)
begin
ii:=0;
h:=LoadLibrary('Projectl.dll'} ;
if h о 0 then
begin
@MyModalForm:=GetProcAddress(h,'HyModalForm');
if @MyModalForm о nil then
MyModalForm(ii,Forml) ;
FreeLibrary(h);
end;
Edit4.Text;= IntToStr(ii); {Отображение значения, возвращаемого
функцией MyModalForm из DLL-библиотеки)
end;
end.