Использование и создание DLL в Delphi. Использование Dll в Delphi DLL, использующие объекты VCL для работы с данными

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

Что конкретно мы рассмотрим:

  1. Как обычно, из области "Hello World", мы создадим свою первую DLL.
  2. Научимся пользоваться функциями этой DLL из своих программ.
  3. Научимся просматривать функции, которые экспортирует определенная DLL.
  4. Может, что нибудь еще....

Процесс создания DLL

Начнем с самого простого - написание своей первой DLL, которая будет содержать всего лишь одну функцию, которая выводит сообщение "Hello World".

  1. Запускаем Delphi (Я использую Delphi 6).
  2. Далее: File -> New ->Other

На закладке New дважды щелкаем по объекту DLL Wizard. Откроется новый проект. Сохраните его, например, с именем MyFirstDLL.

Чистый модуль имеет примерно такое содержание:

Library MyFirstDLL; uses SysUtils, Classes; {$R *.res} begin end.

Теперь напишем всего лишь одну функцию, которая вызовет ShowMessage() из модуля Dialogs. Следовательно, перед началом оформления процедуры допишем в раздел Uses модуль Dialogs. Что, примерно, должно у вас получится:

Library MyFirstDLL; uses Dialogs; procedure MyFirstFunc; stdcall; begin ShowMessage("Hello World"); end; exports MyFirstFunc; begin end.

Как видите, тут нет ничего очень сложного. Единственное скажу, что можно вызывать функции как по имени, так и по индексу (номеру), для этого надо писать так:

Exports MyFirstFunc index 1;

Если что не понятно в этом коде, то все таки постарайтесь разобраться сначала сами. Думаю, что с этим проблем не будет... Но если что, то forum.! Идем дальше, как же можно теперь пользоваться этой (MyFirstFunc) функцией из других проектов?

Использование функций DLL

Первый шаг сделан, дело за малым... Как нам использовать эту функцию?

Есть, как минимум, два способа загрузки:

  1. Статический
  2. Динамический

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

Создаем новый проект, бросаем на форму одну кнопку и по событию OnClick этой кнопки и пишем следующее:

Procedure TForm1.Button1Click(Sender: TObject); begin MyProc(); end;

Но это еще не все! В разделе implementation проекта запишите:

Implementation procedure MyProc(); stdcall; external "MyFirstDLL.dll" name "MyFirstFunc";

Готово! Компилируйте проект и жмите на кнопку! Если показалось ваше сообщение, то все ок!

Теперь рассмотрим способ с динамической загрузкой. Для этого метода используют функцию LoadLibrary(), а в конце, для выгрузки - FreeLibrary().

Посмотрите на примере:

Procedure TForm1.Button1Click(Sender: TObject); type TMyFunc = procedure; var DLLInstance: THandle; MyFunc: TMyFunc; begin DLLInstance:= LoadLibrary(PChar("MyFirstDLL.dll")); if (DLLInstance = 0) then begin MessageDlg("Невозможно загрузить DLL", mtError, , 0); Exit; end; try @MyFunc:= GetProcAddress(DLLInstance, "MyFirstFunc"); if Assigned(@MyFunc) then MyFunc() else MessageDlg("Не найдена искомая процедура!.", mtError, , 0); finally FreeLibrary(DLLInstance); end; end;

После успешной загрузки DLL функцией LoadLibrary(), с помощью GetProcAddress() найдем адрес нашей функции, по которому и будем вызывать нашу процедуру из DLL. В конце, обязательно, надо сделать FreeLibrary(). Это настолько важно, что весь код после успешной загрузки, вполть до FreeLibrary() я заключил в блок try finally. Это гарантирует выполнение FreeLibrary, даже если при выполнении действий внутри блока try except, возникнет непредвиденная ошибка в виде исключения (Exception).

Дело в том, что успешные вызовы LoadLibrary и FreeLibrary обязательно должны быть парными. И вот почему. Система, для каждой загружаемой процессом библиотеки, ведет внутри себя счетчик, который увеличивается на 1 при каждом успешном вызове LoadLibrary. Соответственно, при выполнени FreeLibrary, она уменьшает этот счетчик, и если он становится равным нулю, то это означает что данная библиотека более не нужна данному процессу, и ее можно смело удалить из памяти.

Если-же правило парности не соблюдать, то это может привести либо к преждевременной выгрузке (при лишнем FreeLibrary) библиотеки из памяти, либо к ее "застревании" там (при недостатке FreeLibrary).

При соблюдении же этого правила, можно не заботиться о возможной вложенности вызовов LoadLibrary / FreeLibrary.

Просмотр функций определенной DLL

Теперь посмотрим, как можно извлечь все имена функций из файлов PE формата, к которым и относится DLL. Структуру PE формата мы тут рассматривать не будем, следовательно, и исходник будет без пояснений.

Итак, создайте новый проект, бросьте на форму ListBox, в нём мы будем показывать имена функций.

Вот весь проект:

Unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) lb: TListBox; procedure FormCreate(Sender: TObject); private { Private declarations } cmdline: String; ImageBase: DWord; DosHeader: PImageDosHeader; PeHeader: PImageNtHeaders; PExport: PImageExportDirectory; pname: PDWord; name: PChar; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); procedure FatalOsError; begin ShowMessage(SysErrorMessage(GetLastError())); Abort; end; Var i: Integer; begin try if (ParamCount() IMAGE_DOS_SIGNATURE) then FatalOsError; PEHeader:= PImageNtHeaders(DWord(ImageBase) + DWord(DosHeader^._lfanew)); if (PEHeader^.Signature IMAGE_NT_SIGNATURE) then FatalOsError; PExport:= PImageExportDirectory(ImageBase + DWord(PEHeader^.OptionalHeader.DataDirectory.VirtualAddress)); pname:= PDWord(ImageBase + DWord(PExport^.AddressOfNames)); For i:= 0 to PExport^.NumberOfNames - 1 do begin name:= PChar(PDWord(DWord(ImageBase) + PDword(pname)^)); lb.Items.Add(name); inc(pname); end; finally FreeLibrary(ImageBase); end; except Application.ShowMainForm:= False; Application.Terminate; end; end; end.

Если вы захотите сами разобраться в коде и у вас что-то не будет получаться, то на нашем форуме вам обязательно помогут, заходите!

Прицепляем наш Viewer ко всем DLL

У нас есть готовая DLL с функцией, есть просмотрщик функций. Осталось добавить некой функциональности для удобства дальнейшей работы. Давайте сделаем это.... В проводнике открываем любую папку. Идем в Сервис -> Свойства папки... Переходим на закладку "Типы файлов". В списке ищем формат DLL. Если такого нет, то жмем кнопку "Создать" и в поле "Расширение" пишем - DLL. Жмем ОК. Находим созданный нами тип - DLL. Выделяем его и жмем "Дополнительно". Далее "Создать", в поле "Действии" пишем то, что будет отображаться в контекстном меню, например DLL Viewer. Через обзор ищем нашу программку.

Все готово!

Теперь при клике правой кнопкой мыши по файлу формата DLL в меню будет наш DLL Viewer. Выбираем его и смотрим все функции!

Это все, спасибо за внимание!

Аббревиатура DLL означает "динамически соединяемая библиотека". Dll в Delphi это файл, содержащий необходимые для работы компьютерной программы процедуры и функции, с которым программа соединяется на этапе выполнения.

Казалось бы, все необходимые подпрограммы можно описать в теле программы, зачем нужно создавать дополнительный dll-файл и соединяться с ним во время выполнения? Делается это для большей гибкости создаваемой программы. Ведь со временем могут измениться некоторые алгоритмы обработки данных. Если процедуры обработки будут содержаться в теле программы, её придётся перекомпилировать, и вновь передавать заказчикам достаточно объёмный файл. Передать же небольшой dll-файл, содержащий только несколько процедур, гораздо проще, и даже в автоматическом режиме.

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

Создание DLL библиотеки

Создание dll в Delphi не сложнее создания дополнительного модуля. Выпоните команду File -> New -> -> Other... В появившепся диалоговом окне выберите пиктограмму DLL Wisard. В результате Delphi создаст заготовку проекта библиотеки DLL:

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. }

uses
SysUtils,
Classes;

{$R *.res}

begin
end
.

Выберите имя для новой библиотеки dll и сохраните в отдельную папку, выполнив команду File -> Save As... В папке появятся 4 файла, среди которых собственно dll-файла не будет. Естественно, ведь это просто текстовые файлы, содержащие описание проекта. Для создания итогового файла библиотеки dll необходимо скомпилировать проект. Выполните команду Project -> Compile Project . В результате в нашей папке появится собственно dll файл, с которым и будет соединяться основная программа.

Пока это пустая библиотека.

...в настоящее время текст находится в стадии редактирования...

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

Хочется привести отрывок из «Уроков по Дельфи» о структуре DLL в памяти:

DLL - библиотека, в отличие от приложения не имеет ни стека, ни очереди сообщений. Функции, помещенные в DLL, выполняются в контексте вызвавшего приложения, пользуясь его стеком. Но эти же функции используют сегмент данных, принадлежащий библиотеке, а не копии приложения. В силу такой организации DLL, экономия памяти достигается за счет того, что все запущенные приложения используют один модуль DLL, не включая те или иные стандартные функции в состав своих модулей. Часто, в виде DLL создаются отдельные наборы функций, объединенные по тем или иным логическим признакам, аналогично тому, как концептуально происходит планирование модулей (в смысле unit) в Pascal. Отличие заключается в том, что функции из модулей Pascal компонуются статически - на этапе линковки, а функции из DLL компонуются динамически, то есть в run-time.

Создание DLL

Структура DLL мало чем отличается от обычной структуры модуля в Object Pascal. Начинаться DLL должна со слова Library, за которым помещается название библиотеки. Функции и процедуры, которые DLL будет предоставлять другим пользователям (экспортировать), перечисляются после директивы exports.

Для каждой процедуры или функции можно указать ее номер с помощью директивы Index. Если номер будет отсутствовать, то компилятор проведет автоматическую индексацию. Вместо номера процедуры можно использовать уникальное имя, которое задается с помощью директивы name. Если же не указано ни имени функции, ни ее номера, то Дельфи воспримет это как экспорт по имени, которое будет совпадать с названием функции.

Библиотека может содержать также и код инициализации, который будет выполнен при ее загрузке. Он помещается между begin и end. Вот общая структура DLL:

Library First_Dll; uses <используемые модули>; <объявления и описания функций> exports <экспортируемые функции> <описание процедур и функций> begin <инициализационная часть> end.

Приведу примеры описания экспортируемых функций в разделе exports:

Exports Function1 index 2; Fucntion2 name "My_sqr"; Function3;

Как Вы уже догадались, имя функции может не совпадать с именем для экспорта!!!

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

Модуль, в котором необходимо использовать процедуры и функции из DLL должен использовать директиву external. Существует два способа использования DLL (динамический и статический). В первом случае, приложение вызывающее функцию из DLL знает имя библиотеки и точку входа в нее, при этом предполагается, что библиотека не меняется. Во втором случае перед использованием DLL следует убедиться, что требуемая библиотека существует, и в ней есть необходимая подпрограмма.

Импортировать подпрограмму можно по ее имени и номеру. Поиск подпрограммы по номеру происходит быстрее, но всегда это удобно.

Ниже приведены примеры импорта функций из нашей First_DLL, которая была рассмотрена в примере:

{ импорт по специфицированному имени } Function ImportByName; external "First_DLL" name "My_sqr"; { импорт по индексу } Function ImportByOrdinal; external "First_DLL" index 2; { импорт по оригинальному имени } Function Fucntion3; external "First_DLL";

Мы рассмотрели статический метод использования DLL. Но в некоторых случаях заранее не известно какая именно потребуется библиотека, поэтому следует воспользоваться динамическим методом:

Uses WinTypes, WinProcs, ... ; type TMyProc = procedure ; var Handle: THandle; MyImportProc: TMyProc; begin Handle:=LoadLibrary("FirstDLL"); if Handle>=32 then { если <=32 - ошибка! } begin @MyImportProc:=GetProcAddress(Handle,"My_sqr"); if MyImportProc<>nil then ...... {здесь используем полученную функцию} end; FreeLibrary(Handle); end;

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

Нажмите в меню File -> New и выберите DLL. Сохраните готовый шаблон, как предлагается, под именем Project1.dpr.

Ниже приведен его полный код:

Library Project1; uses SysUtils,Classes; Function Function1(x,y:integer):integer; export; bgin result:=x+y; end; Function Function2(x,y:real):real; export; var t:real; begin t:=exp(y*ln(x)); result:=t; end; exports Function1 index 1, Function2 name "My_sqr"; begin end.

Здесь две функции, первая вычисляет сумму двух чисел и экспортируется по номеру, а вторая вычисляет x в степени y и экспортируется по имени.

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

Unit demo; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} function ImportSumm(x,y:integer):integer; external "Project1" index 1; //импорт по номеру function ImportSqr(x,y:real):real; external "Project1" name "My_sqr"; //импорт по имени procedure TForm1.Button1Click(Sender: TObject); var t:real; begin //Узнаем сколько же будет два в третьей степени t:=ImportSqr(2,3); Showmessage(FloatTostr(t)); end; procedure TForm1.Button2Click(Sender: TObject); var t:integer; begin //Узнаем сколько же будет 10+10 t:=ImportSumm(10,10); Showmessage(IntTostr(t)); end; end.

Программист Delphi, MySQL. Образование: высшее. Специальность: программное обеспечение информационных технологий.

Здравствуйте уважаемые коллеги!

В данной статье я постараюсь ответить на вопросы: Что такое 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
  • Создание DLL в Delphi (экспорт)
  • Использование DLL в Delphi (импорт)
  • DLL, использующие объекты VCL для работы с данными
  • Исключительные ситуации в DLL
  • Понятие DLL

Вспомним процесс программирования в DOS. Преобразование исходного текста программы в машинный код включал в себя два процесса - компиляцию и линковку. В процессе линковки, редактор связей, компоновавший отдельные модули программы, помещал в код программы не только объявления функций и процедур, но и их полный код. Вы готовили таким образом одну программу, другую, третью... И везде код одних и тех же функций помещался в программу полностью.

Программа1 Программа2: : MyFunc(:) MyFunc(:) : : код функции MyFunc код функции MyFunc код других функций код других функций

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

Но, чем же отличаются Dynamic Link Library (DLL) от обычных приложений? Для понимания этого требуется уточнить понятия задачи (task), экземпляра (копии) приложения (instance) и модуля (module).

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

DLL - библиотека также является модулем. Она находится в памяти в единственном экземпляре и содержит сегмент кода и ресурсы, а также сегмент данных (см. рис. 4).

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

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

Часто, в виде DLL создаются отдельные наборы функций, объединенные по тем или иным логическим признакам, аналогично тому, как концептуально происходит планирование модулей (в смысле unit) в Pascal. Отличие заключается в том, что функции из модулей Pascal компонуются статически - на этапе линковки, а функции из DLL компонуются динамически, то есть в run-time.

Создание DLL в Delphi (экспорт)

Для программирования DLL Delphi предоставляет ряд ключевых слов и правил синтаксиса. Главное - DLL в Delphi такой же проект как и программа.

Рассмотрим шаблон DLL:


Имя файла проекта для такого шаблона должно быть MYDLL.DPR.

К сожалению, в IDE Delphi автоматически генерируется только проект программы, поэтому Вам придется проект DLL готовить вручную. В Delphi 2.0 это неудобство устранено.

Как и в программе, в DLL присутствует раздел uses. Инициализационная часть необязательна. В разделе же exports перечисляются функции, доступ к которым должен производится из внешних приложений.

Экспортирование функций (и процедур) может производится несколькими способами:

  • по номеру (индексу)
  • по имени

В зависимости от этого используется различный синтаксис:


Так как в Windows существует понятие "резидентных функций" DLL, то есть тех функций, которые находятся в памяти на протяжении всего времени существования DLL в памяти, в Delphi имеются средства для организации и такого рода экспорта:


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

Использование DLL в Delphi (импорт)

Для организации импорта, т.е. доступа к функциям, экспортируемым из DLL, так же как и для их экспорта, Delphi предоставляет стандартные средства.

Для показанных выше примеров, в Вашей программе следует объявить функции, импортируемые из DLL таким образом:


Этот способ называется статическим импортом.

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

Для решения такого рода проблем Вы можете обратиться напрямую к API Windows, используя, так называемый, динамический импорт:


uses WinTypes, WinProcs, ... ; type TMyProc = procedure ; var Handle: THandle; MyImportProc: TMyProc; begin Handle:= LoadLibrary("MYDLL"); if Handle >= 32 then { if begin @MyImportProc:= GetProcAddress(Handle, "MYEXPORTPROC"); if MyImportProc nil then ... {using imported procedure} end ; FreeLibrary(Handle); end ;

Синтаксические диаграммы объявлений экспорта/импорта, подмена точки выхода из DLL, и другие примеры, Вы можете найти в OnLine Help Delphi, Object Pascal Language Guide, входящему в Borland RAD Pack for Delphi, и, например, в книге "Teach Yourself Delphi in 21 Days".

Если не говорить о генерируемом компилятором коде (сейчас он более оптимизирован), то все правила синтаксиса остались те же, что и в Borland Pascal 7.0

DLL, использующие объекты VCL для работы с данными

При создании своей динамической библиотеки Вы можете использовать вызовы функций из других DLL. Пример такой DLL есть в поставке Delphi (X:\DELPHI\DEMOS\BD\BDEDLL). В эту DLL помещена форма, отображающая данные из таблицы и использующая для доступа к ней объекты VCL (TTable, TDBGrid, TSession), которые, в свою очередь, вызывают функции BDE. Как следует из комментариев к этому примеру, для такой DLL имеется ограничение: ее не могут одновременно использовать несколько задач. Это вызвано тем, что объект Session, который создается автоматически при подключении модуля DB, инициализируется для модуля, а не для задачи. Если попытаться загрузить эту DLL вторично из другого приложения, то возникнет ошибка. Для предотвращения одновременной загрузки DLL несколькими задачами нужно осуществить некоторые действия. В примере - это процедура проверки того, используется ли DLL в данный момент другой задачей.

Исключительные ситуации в DLL

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


function MyFunc: string ; begin try {собственно код функции} except on EResult: Exception do Result:=Format(DllErrorViewingTable, ) else Result:= Format(DllErrorViewingTable, ["Unknown error"]); end ; end ;

Код в программе:


StrResult:= MyFunc; if StrResult "" then raise Exception.Create(StrResult);

Публикации по теме