Динамические библиотеки dll делфи примеры. Создание DLL-библиотеки на Delphi

29.03.2019

Чтобы создать новую библиотеку DLL в Delphi, выберите команду меню File > New > Other . В панели Items Categories окна New Items выберите узел Delphi Projects , после чего дважды щелкните на элементе Dynamic-link Library в правой панели окна.

Мастер DLL Wizard создает главный файл исходного кода библиотеки DLL, который выглядит практически так же, как и исходный код, сгенерированный для обычного приложения. Единственное отличие состоит в том. что этот файл начинается с зарезервированного слова library , а не program .

Library Project1; { Important note about DLL memory management: ShareMem must be the first unit in your library"s USES clause AND your project"s (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. } { Важное замечание относительно управления памятью библиотеки DLL: модуль ShareMem должен быть первым модулем в операторе uses вашей библиотеки и в операторе uses вашего проекта (выберите команду меню Project -> View Source (Проект -> Показать исходный код)), если ваша библиотека DLL экспортирует какие-либо процедуры или функции, передающие строки в качестве параметров или результатов выполнения функций. Это относится ко всем строкам, передаваемым или получаемым из вашей библиотеки DLL, и даже к тем строкам, которые вложены в записи и классы. Модуль ShareMem является модулем интерфейса для администратора общей памяти BORLNDMM.DLL, который вы должны развертывать вместе со своей библиотекой DLL. Чтобы не использовать BORLNDMM.DLL, передавайте строковую информацию с помощью параметров PChar или ShortString. } uses SysUtils, Classes; {$R *.res} begin end.

Все, что вам нужно сделать сейчас - это добавить подпрограмму перед блоком begin-end , вот и все. После этого вы получите внутреннюю подпрограмму, которую можно будет использовать в библиотеке DLL, но не во внешних приложениях. Если вы хотите вызывать подпрограмму из других приложении и других библиотек DLL, ее потребуетсл экспортировать. Чтобы экспортировать подпрограмму по имени, добавьте ее в список exports . Список exports имеет такой же синтаксис, что и список uses , за исключением того, что в списке exports любой элемент является подпрограммой, а не модулем.

Список exports обычно ставится непосредственно перед блоком begin-end . Взгляните на листинг 1, в котором представлен исходный код простой библиотеки FirstLib.dll , экспортирующей одну функцию.

Листинг 1. Простая библиотека DLL

Library FirstLib; function Max3(Num1, Num2, Num3: Integer): Integer; stdcall; begin Result:= Num1; if Num2 > Result then Result:= Num2; if Num3 > Result then Result:= Num3; end; { Экспортируем функцию Max3 } exports Max3; begin end.

Когда вы добавите подпрограмму в список exports , вы тем самым экспортируете подпрограмму по ее имени. Вы можете также экспортировать подпрограмму под другим именем с помощью директивы name или посредством порядкового значения, используя директиву index . Однако применять директиву index не рекомендуется.

Ниже показан пример экспортирования директивы посредством порядкового значения или под другим именем:

Exports Max3 name "MyMax3Function", SaySomething index 1;

Статическая загрузка является самым простым из двух возможных способов загрузки библиотеки DLL. Статическую загрузку называют еще динамическим подключением во время загрузки (load-time dynamic linking ), потому что используемые библиотеки DLL автоматически загружаются во время запуска приложения.

Чтобы статически загрузить библиотеку DLL, необходимо скопировать объявление подпрограммы и вызывающее приложение и пометить ее с помощью директивы external , которая сообщает компилятору о том, что подпрограмма находится либо в объектном файле, либо в библиотеке DLL.

Когда вы импортируете подпрограммы из библиотеки DLL, вы должны помечать их директивой external , указывая вслед за ней имя библиотеки DLL, в которой содержится реализация подпрограммы. Далее показан пример импорта функции МахЗ из библиотеки FirstLib.dll :

Function Max3(Num1, Num2, Num3: Integer): Integer; stdcall; external "FirstLib.dll";

При желании можете даже переименовать подпрограмму во время ее импорта. Для этого необходимо объявить подпрограмму под другим именем, а в конце объявления указать первоначальное имя с помощью директивы name :

Function Max(Num1, Num2, Num3: Integer): Integer; stdcall; external "FirstLib.dll" name "Max3";

Вы можете также импортировать функцию из библиотеки DLL. Для этого нужно создать модуль импорта и написать стандартный заголовок подпрограммы в разделе interface , а ее внешнюю реализацию - в разделе implementation этого модуля. В листинге 2 показан полный код модуль импорта библиотеки FirstLib.dll .

Листинг 2. Модуль импорта библиотеки FirstLib.dll.

Unit FirstLibInf; interface function Max3(Num1, Num2, Num3: Integer): Integer; stdcall; implementation const FirstLib = "FirstLib.dll"; {Компилятору сообщается о том, что реализация функции Max3 находится в библиотеке FirstLib.dll} function Max3; external FirstLib; end.

После того как вы создадите библиотеку DLL и ее модуль импорта, протестируйте библиотеку DLL, чтобы убедиться в том. что подпрограмма работает нормально. Поскольку запустить саму библиотеку DLL нельзя, нужно создать тестовое приложение, которое будет обращаться к библиотеке DLL. Быстрее всего сделать это можно, если создать проектную группу, добавив новый проект в текущий проект. Для этого потребуется щелкнуть правой кнопкой мыши на элементе ProjectGroup1 в окне Project Manager (Диспетчер проекта) и выбрать в контекстном меню команду Add New Project , как показано на рис. 1.

Листинг 3. Проверка подпрограммы Max3, импортированной из библиотеки FirstLib.dll.

Unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, FirstLibInf, StdCtrls; type TMainForm = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Max3Button: TButton; procedure Max3ButtonClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.Max3ButtonClick(Sender: TObject); var LargestNumber: Integer; begin LargestNumber:= Max3(StrToInt(Edit1.Text), StrToInt(Edit2.Text), StrToInt(Edit3.Text)); MessageDlg(Format("Наибольшее число: %d.", ), mtInformation, , 0); end; end.

Используемая литература: Внутренний мир Borland Delphi 2006. Иван Хладни.

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

Естественно, используя Delphi, мы можем тоже написать и использовать наши собственные DLL и можем вызывать функции в DLL , разработанном другими разработчиками и на других системах (типа Visual Basic или C/C++ ).

Создание динамической библиотеки

Следующие несколько строк демонстрируют, как создать простой DLL , используя Delphi.

Начинающим Дельфистам нужно из меню выбрать File | New... DLL . Это создаст шаблон нового DLL в окне редактора. Выделите заданный по умолчанию текст и замените его следующим кодом:

Library TestLibrary; uses SysUtils, Classes, Dialogs; procedure DllMessage; export; begin ShowMessage("Hello world from a Delphi DLL") ; end; exports DllMessage; begin end.

Если Вы посмотрите на файл проекта любого приложения Delphi, Вы увидите, что он начинается с зарезервированного слова Program . DLL в отличие от этого, начинается с зарезервированного слова Library . Затем это сопровождается пунктом uses для любых необходимых модулей. В этом простом примере за этим следует процедура по имени DllMessage , которая не делает ничего, кроме показа простого сообщения.

В конце этого текста, стоит инструкция exports . Здесь включен список подпрограмм, которые будут экспортированы из DLL и могут вызываться другими приложениями. Это означает, что, например, в DLL находится 5 процедур и только 2 из них (перечисленных в разделе exports ) могут вызываться из внешних программ (3 из них подпрограммы в DLL ).

Чтобы использовать этот простой DLL , мы должны скомпилировать его, нажав Ctrl+F9 . Это создасть DLL по имени SimpleMessageDll.dll в папке проекта.


А теперь давайте посмотрим, как вызвать процедуру DllMessage из (статически загруженной) DLL .

Чтобы импортировать процедуру, содержащуюся в DLL , мы используем ключевое слово external в объявлении процедуры. Например, объявление процедуры DllMessage , показанной ранее, выглядела бы так:

Procedure DllMessage; external "SimpleMessageDLL.dll"

А вызов процедуры выглядит так:

DllMessage;

Полный код для формы Form1 с TButton по имени Button1 (для вызова функции DllMessage ) выглядит так:

Unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject) ; private { Private declarations } public { Public declarations } end; var Form1: TForm1; procedure DllMessage; external "SimpleMessageDLL.dll" implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject) ; begin DllMessage; end; end.

Это все! Все просто, как и все в Delphi!

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 передается при вызове процедуры и содержит указатель

на родительскую форму - форму вызывающего приложения}

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

Прежде чем начать использование какой-либо процедуры или функции, находящейся в динамической библиотеке,
вам необходимо загрузить DLL в оперативную память. Загрузка библиотеки может быть осуществлена
одним из двух способов: статическая загрузка и динамическая загрузка.
Оба метода имеют как преимущества, так и недостатки.
Статическая загрузка означает, что динамическая библиотека загружается автоматически
при запуске на выполнение использующего ее приложения. Для того чтобы использовать такой способ загрузки,
вам необходимо воспользоваться ключевым словом external при описании экспортируемой из
динамической библиотеки функции или процедуры. DLL автоматически загружается при старте программы,
и Вы сможете использовать любые экспортируемые из нее подпрограммы точно так же,
как если бы они были описаны внутри модулей приложения.
Это наиболее легкий способ использования кода, помещенного в DLL .
Недостаток метода заключается в том, что если файл библиотеки, на который
имеется ссылка в приложении, отсутствует, программа откажется загружаться.
Смысл динамического метода заключается в том, что вы загружаете библиотеку не при старте приложения,
а в тот момент, когда вам это действительно необходимо. Сами посудите, ведь если функция, описанная
в динамической библиотеке, используется только при 10% запусков программы, то совершенно нет
смысла использовать статический метод загрузки. Выгрузка библиотеки из памяти в данном случае
также осуществляется под вашим контролем. Еще одно преимущества такого способа
загрузки DLL - это уменьшение (по понятным причинам) времени старта вашего приложения.
А какие же у этого способа имеются недостатки? Основной, как мне кажется, - это то, что использование
данного метода является более хлопотным, чем рассмотренная выше статическая загрузка.
Сначала вам необходимо воспользоваться функцией Windows API LoadLibrary .
Для получения указателя на экспортируемой процедуры или функции должна
использоваться функция GetProcAddress. После завершения использования библиотеки DLL
должна быть выгружена с применением FreeLibrary.
Вызов процедур и функций, загруженных из DLL.
Способ вызова процедур и функций зависит от того, каким образом вы загрузили динамическую библиотеку,
в которой эти подпрограммы находятся.
Вызов функций и процедур из статически загруженных DLL достаточно прост. Первоначально в приложении
должно содержаться описание экспортируемой функции (процедуры). После этого вы можете их использовать
точно так же, как если бы они были описаны в одном из модулей вашего приложения.
Для импорта функции или процедуры, содержащейся в DLL , необходимо использовать
модификатор external в их объявлении. К примеру, для рассмотренной нами выше процедуры HelloWorld
в вызывающем приложении должна быть помещена следующая строка:
procedure SayHello(AForm: TForm); external myfirstdll.dll";
Ключевое слово external сообщает компилятору, что данная процедура может быть найдена в
динамической библиотеке (в нашем случае - myfirstdll.dll).
Далее вызов этой процедуры выглядит следующим образом:
...
HelloWorld(self);
...
При импорте функции и процедур будьте особенно внимательны при написании их имен и интерфейсов!
Дело в том, что в процессе компиляции приложения не производится проверки на правильность имен объектов,
экспортируемых из DLL, осуществляться не будет, и если вы неправильно описали какую-нибудь функцию,
то исключение будет сгенерировано только на этапе выполнения приложения.
Импорт из DLL может проводиться по имени процедуры (функции), порядковому номеру или
с присвоением другого имени.
В первом случае вы просто объявляете имя процедуры и библиотеку, из которой ее импортируете
(мы это рассмотрели чуть выше). Импорт по порядковому номеру требует от вас указание этого самого номера:
procedure HelloWorld(AForm: TForm); external myfirstdll.dll index 15;
В этом случае имя, которое вы даете процедуре при импорте не обязательно должно совпадать с тем,
которое было указано для нее в самой DLL. Т.е. приведенная выше запись означает,
что вы импортируете из динамической библиотеки myfirstdll.dll процедуру, которая в ней экспортировалась
пятнадцатой, и при этом в рамках вашего приложения этой процедуре дается имя SayHello.
Если вы по каким-то причинам не применяете описанный выше способ импорта,
но тем не менее хотите изменить имя импортируемой функции (процедуры), то можно воспользоваться третьим методом:
procedure CoolProcedure; external myfirstdll.dll name "DoSomethingReallyCool";
Здесь импортируемой процедуре CoolProcedure дается имя DoSomethingReallyCool.
Вызов процедур и функций, импортируемых из динамически загружаемых библиотек
несколько более сложен, чем рассмотренный нами выше способ. В данном случае требуется объявить
указатель на функцию или процедуру, которую вы собираетесь использовать.
Помните процедуру HelloWorld? Давайте посмотрим, что необходимо сделать для того,
чтобы вызвать ее на выполнение в случае динамической загрузки DLL. Во-первых, вам
необходимо объявить тип, который описывал бы эту процедуру:
type
THelloWorld = procedure(AForm: TForm);
Теперь вы должны загрузить динамическую библиотеку, с помощью GetProcAddress получить
указатель на процедуру, вызвать эту процедуру на выполнение, и, наконец, выгрузить DLL из памяти.
Ниже приведен код, демонстрирующий, как это можно сделать:

DLLInstance: THandle ;

HelloWorld: THelloWorld;

begin

{ загружаем DLL }

{ получаем указатель }

{ вызываем процедуру на выполнение }

HelloWorld(Self ) ;

{ выгружаем DLL из оперативной памяти }

FreeLibrary(DLLInstance) ;

end ;

Как уже говорилось выше, одним из недостатков статической загрузки DLL является невозможность
продолжения работы приложения при отсутствии одной или нескольких библиотек. В случае с динамической
загрузкой у вас появляется возможность программно обрабатывать такие ситуации и не допускать, чтобы программа
вываливалась» самостоятельно. По возвращаемому функциями LoadLibrary и GetProcAddress значениям можно
определить, успешно ли прошла загрузка библиотеки и найдена ли в ней необходимая приложению процедура.
Приведенный ниже код демонстрирует это.

procedure TForm1.DynamicLoadBtnClick (Sender: TObject ) ;

type

THelloWorld = procedure (AForm: TForm) ;

DLLInstance: THandle ;

HelloWorld: THelloWorld;

begin

DLLInstance:= LoadLibrary("myfirstdll.dll" ) ;

if DLLInstance = 0 then begin

MessageDlg("Невозможно загрузить DLL" , mtError, [ mbOK] , 0 ) ;

Exit ;

end ;

@HelloWorld:= GetProcAddress(DLLInstance, "HelloWorld" ) ;

if @HelloWorld nil then

HelloWorld (Self )

else

MessageDlg("Не найдена искомая процедура!." , mtError, [ mbOK] , 0 ) ;

FreeLibrary(DLLInstance) ;

end ;

В DLL можно хранить не только код, но и формы.
Причем создание и помещение форм в динамическую библиотеку не слишком сильно отличается от работы
с формами в обычном проекте. Сначала мы рассмотрим, каким образом можно написать библиотеку,
содержащую формы, а затем мы поговорим об использовании технологии MDI в DLL.
Разработку DLL, содержащую форму, я продемонстрирую на примере.
Итак, во-первых, создадим новый проект динамической библиотеки.
Для этого выберем пункт меню File|New, а затем дважды щелкнем на иконку DLL .
После этого вы увидите примерно следующий код:

Сохраните полученный проект. Назовем его DllForms.dpr.
Теперь следует создать новую форму. Это можно сделать по-разному.
Например, выбрав пункт меню File|New Form. Добавьте на форму какие-нибудь компоненты.
Назовем форму DllForm и сохраним получившийся модуль под именем DllFormUnit.pas .
Вернемся к главному модулю проекта и поместим в него функцию ShowForm, в задачу которой будет входить
создание формы и ее вывод на экран. Используйте для этого приведенный ниже код.

Form: TDLLForm;

begin

Result:= Form.ShowModal ;

Form.Free ;

end ;

Обращаю внимание, что для того, чтобы проект был скомпилирован без ошибок, необходимо добавить в секцию uses модуль Forms .
Экспортируем нашу функцию с использованием ключевого слова exports:
exports
ShowForm;
Компилируем проект и получаем файл dllforms.dll. Эти простые шаги - все,
что необходимо сделать для сОбратите внимание, что функция ShowForm объявлена с использованием ключевого слова stdcall .
Оно сигнализирует компилятору использовать при экспорте функции соглашение
по стандартному вызову (standard call calling convention). Экспорт функции таким образом создает
возможность использования разработанной DLL не только в приложениях, созданных в Delphi.
Соглашение по вызову (Calling conventions) определяет, каким образом передаются аргументы при вызове функции.
Существует пять основных соглашений: stdcall, cdecl, pascal, register и safecall.
Подробнее об этом можно узнать, посмотрев раздел " Calling Conventions " в файле помощи Delphi.
Также обратите внимание, что значение, возвращаемое функцией ShowForm ,
соответствует значению ShowModal. Таким образом вы можете передавать некоторую информацию
о состоянии формы вызывающему приложению.
Ниже представлено два листинга, первый из которых содержит полный код файла
проекта DLL (модуль с формой здесь не приводится), а второй - модуль вызывающего приложения,
в котором используется только что разработанная нами библиотека.

library DllForms;

uses

DllFormUnit in "DllFormUnit.pas" {DllForm} ;

{$R *.RES}

function ShowForm: Integer ; stdcall ;

Form: TDLLForm;

begin

Form:= TDLLForm.Create (Application) ;

Result:= Form.ShowModal ;

Form.Free ;

end ;

begin

end .


unit TestAppUnit;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls;

type

TForm1 = class (TForm)

Button1: TButton;

procedure Button1Click(Sender: TObject ) ;

private

{ Private declarations }

public

{ Public declarations }

end ;

Form1: TForm1;

function ShowForm: Integer ; stdcall ;

External "dllforms.dll" ;

implementation

{$R *.DFM}

procedure TForm1.Button1Click (Sender: TObject ) ;

begin

end ;

end .

Прошу заметить, что при экспорте функции также было использовано ключевое слово stdcall.
Следует обратить особое внимание на работу с дочерними формами в DLL. Если, к примеру,
в вызывающем приложении главная форма имеет значение свойства FormStyle, равным MDIForm,
то при попытке вызова из DLL MDIChild-формы, на экране появится сообщение об ошибке,
в котором будет говориться, что нет ни одной активной MDI-формы.
В тот момент, когда вы пытаетесь показать ваше дочернее окно, VCL проверяет корректность
свойства FormStyle главной формы приложения. Однако в нашем случае все вроде бы верно.
Так в чем же дело? Проблема в том, что при проведении такой проверки, рассматривается объект Application,
принадлежащий не вызывающему приложению, а собственно динамической библиотеке.
Ну, и естественно, поскольку в DLL нет главной формы, проверка выдает ошибку.
Для того чтобы избежать такой ситуации, надо назначить объекту Application динамической библиотеки
объект Application вызывающего приложения. Естественно, это заработает только в том случае,
когда вызывающая программа - VCL-приложение. Кроме того, перед выгрузкой библиотеки из памяти
необходимо вернуть значение объекта Application библиотеки в первоначальное состояние.
Это позволит менеджеру памяти очистить оперативную память, занимаемую библиотекой.
Следовательно, вам нужно сохранить указатель на «родной» для библиотеки объект Application
в глобальной переменной, которая может быть использована при восстановлении его значения.
Итак, вернемся немного назад и перечислим шаги, необходимые нам для работы с помещенным
в DLL MDIChild-формами.
В динамической библиотеке создаем глобальную переменную типа TApplication.
Сохраняем указатель на объект Application DLL в глобальной переменной.
Объекту Application динамической библиотеки ставим в соответствие указатель на Application
вызывающего приложения.
Создаем MDIChild-форму и работаем с ней.
Возвращаем в первоначальное состояние значение объекта Application динамической библиотеки
и выгружаем DLL из памяти.
Первый шаг прост. Просто помещаем следующий код в верхней части модуля DLL:
var
DllApp: TApplication;
Затем создаем процедуру, которая будет изменять значение объекта Application и создавать дочернюю форму.
Процедура может выглядеть примерно так:

procedure ShowMDIChild(MainApp: TApplication) ;

Child: TMDIChild;

begin

if not Assigned (DllApp) then begin

DllApp:= Application;

Application:= MainApp;

end ;

Child:= TMDIChild.Create (Application.MainForm ) ;

Child.Show ;

end ;

Все, что нам теперь необходимо сделать, - это предусмотреть возвращение значения объекта Application
в исходное состояние. Делаем это с помощью процедуры MyDllProc:

procedure MyDLLProc(Reason: Integer ) ;

begin

if Reason = DLL_PROCESS_DETACH then

{ DLL is выгружается. Восстанавливаем значение указателя Application}

if Assigned (DllApp) then

Application:= DllApp;

end ;

Вместо заключения.
Использование динамически подключаемых библиотек не так сложно, как это может показаться на первый взгляд.

Использование динамически подключаемых библиотек не так сложно, как это может показаться на первый взгляд.
DLL предоставляют широчайшие возможности для оптимизации работы приложений,
а также работы самих программистов. Используйте DLL и, возможно, ваша жизнь станет легче!
http://subscribe.ru/
E-mail: [email protected] Поиск
на АПОРТ на Subscribe.Ru

Модульные программы

При разработке программ часто оказывается, что разным программным приложениям требуются одни и те же объекты, их свойства методы, процедуры и функции. Например, почти все программы выводят информацию на экран и пользуются стандартными объектами интерфейса Windows (окна, кнопки, меню…) Было бы в высшей степени неразумно запихивать код отрисовки каждого такого элемента во все программы.

Таким образом, возникает задача разделения большой программы на отдельные независимые модули, каждый из которых содержит определенный набор процедур и функций. Процедуры и функции такого модуля может вызывать любая другая программа. Подобные модули получили название динамически подключаемых библиотек (DLL – dynamically linked library, Рис. 19.1). Слово "динамический" указывает на то, что подключение библиотеки происходит не на этапе компиляции, а уже после запуска программы.

Рис. 23.1 DLL – библиотеки.

DLL-библиотеки нашли самое широкое применение в большинстве программ. Скажем, сама операционная система Windows включает в свой состав несколько сотен DLL, и заключенная в них функциональность может использоваться нашими программами во избежание очередного изобретения велосипеда.

В каких случаях следует применять DLL? Во-первых, если вы разрабатываете несколько независимых программ и в них используются одни и те же фрагменты программного кода. Тогда такие фрагменты следует вынести в библиотеку. Это не только экономит место на диске, но и облегчает внесение изменений в проект – придется править только одну DLL, а не много exe-программ. Во-вторых, если вы хотите дать возможность пользователям вашей гениальной программы применять ее в своих разработках. В этом случае программу обычно разделяют на маленький exe-файл и большую DLL-библиотеку. Другие разработчики смогут использовать вашу DLL-библиотеку при условии, что вы предоставите описание содержащихся в ней процедур и функций и их параметров. Принято говорить, что процедуры и функции DLL-библиотеки экспортируются в другие программы.

В среде программирования Delphi предусмотрены встроенные средства для быстрого создания DLL-библиотек. Давайте для определенности создадим библиотеку, содержащую функцию GetArea(a, b, c: REAL):REAL. Данной функции на вход подаются длины сторон треугольника. Функция возвращает площадь заданного треугольника:

FUNCTION GetArea(a, b, c:REAL):REAL;

Result:=SQRT(p*(p-a)*(p-b)*(p-c))

Запускаем Delphi, а дальше действуем нетрадиционно. Выбираем пункты меню File à New à Other, в открывшемся окошке на закладке New щелкаем по пиктограмме DLL Wizard . При этом создается файл заготовки DLL-библиотеки. Он очень похож на обычный модуль (unit) Delphi, только начинается с оператора Library. Сохраните проект под именем, которое в будущем получит DLL-библиотека, скажем, GetA. Название GetArea использовать нельзя – оно уже занято именем функции.

Теперь после оператора USES пишем текст нашей функции, но с некоторыми изменениями в заголовке:

FUNCTION GetArea(a, b, c:REAL):REAL; export; stdcall;

Ключевое слово EXPORT указывает на то, что данная функция является экспортируемой и будет видна из внешних программ. Слово STDCALL (standard call – стандартная модель вызова) указывает на применяемый протокол передачи параметров в функцию. Стандартная модель вызова совместима с программами, написанными на других языках, в частности, на С, поэтому нашу DLL смогут использовать и программисты, использующие С.

После текста функции припишем

В операторе EXPORTS перечисляются все процедуры и функции, экспортируемые из библиотеки. Это своего рода каталог нашей библиотеки.

Запустить на выполнение библиотеку невозможно, ее можно только откомпилировать. Для этого выполним пункт меню Project à Build. Если все было сделано правильно, на диске в текущей директории будет создан файл с именем geta.dll. Это и есть наша библиотека.

Важное замечание : существует определенная тонкость при передаче процедурам и функциям, находящимся в библиотеке, параметров типа STRING. Для того, чтобы можно было передавать параметры типа STRING, придется в операторы USES и библиотеки, и вызывающей ее программы прописать подключение модуля ShareMem, да еще и обязательно так, чтобы этот модуль шел первым в списке. Мало того, вместе с библиотекой придется обязательно прилагать файл borlndmm.dll (он входит в поставку Delphi). Избежать таких хлопот очень просто: следует для параметров текстового типа использовать типы данных ShortString (фактически обычная строка, но длиной до 255 символов) и PChar (указатель на текстовую строку).