Создание и использование 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.