В связи с бурным развитием технологий программирования, все больше людей сталкиваются с проблемой наращивания возможностей своих программ. Данная статья посвящена именно этому вопросу, а именно - программирование 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 . Подобно модулям, 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 ?
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
- Dynamic Link Library иначе динамически подключаемая библиотека, которая позволяет многократно применять одни и те же функции в разных программах. На самом деле довольно удобное средство, тем более что однажды написанная библиотека может использоваться во многих программах. В сегодняшнем уроке мы научимся работать с dll
и конечно же создавать их!
Ну что ж начнём!
Для начала создадим нашу первую Dynamic Link Library! Отправляемся в Delphi
и сразу же лезем в меню File -> New ->Other.
Перед нами появляется вот такое окошко:
Выбираем в списке Dynamic-Link Library (в версиях младше 2009 Delphi пункт называется DLL Wizard).
В результате у нас появляется лишь окно с кодом, заметьте никакой формы у нас здесь нет!
Теперь начинается самое интересное. Напишем наши первые процедуры в библиотеке.
library
Project2;
//Вы, наверное уже заметили, что вместо program
//при создании dll используется слово library.
//Означающее библиотека.
uses
SysUtils, dialogs,
Classes; //
Внимание!
Не забудьте указать эти модули,
// иначе код работать не будет
{$R *.res}
{В ЭТУ ЧАСТЬ ПОМЕЩАЕТСЯ КОД DLL}
Procedure
FirstCall; stdcall; export;
//Stdcall - При этом операторе параметры помещаются в стек
//справа налево, и выравниваются на стандартное значение
//Экспорт в принципе можно опустить, используется для уточнения
//экспорта процедуры или функции.
Begin
ShowMessage("Моя первая процедура в dll
");
End;
Procedure
DoubleCall; stdcall; export;
Begin
ShowMessage("Моя вторая процедура
");
//Вызываем сообщение на экран
End;
Exports
FirstCall, DoubleCall;
//В Exports содержится список экспортируемых элементов.
//Которые в дальнейшем будут импортироваться какой-нибудь программой.
begin
End.
На этом мы пока остановимся т.к. для простого примера этого будет вполне достаточно. Сейчас сохраняем наш проект, лично я сохранил его под именем Project2.dll и нажимаем комбинацию клавиш CTRL+F9 для компиляции библиотеки. В папке, куда вы сохранили dpr файл обязан появится файл с расширением dll, эта и есть наша только что созданная библиотека. У меня она называется Project2.dll
Займёмся теперь вызовом процедур из данной библиотеки. Создаём по стандартной схеме новое приложение. Перед нами ничего необычного просто форма. Сохраняем новое приложение в какую-нибудь папку. И в эту же папку копируем только что созданную dll библиотеку . Т.е. в данном примере Project2.dll
Теперь вам предстоит выбирать, каким способом вызывать функции из библиотеки. Всего существует два метода вызова.
Способ № 1
Пожалуй, это самый простой метод вызова процедур находящихся в библиотеке.
Идеально подходит для работы только с одной библиотекой.
Ну что поехали...
После ключевого слова implementation
прописываем следующий код:
Procedure
FirstCall; stdcall; external
"Project2.dll"
;
// Вместо Project2.dll может быть любое имя библиотеки
Procedure
DoubleCall; stdcall; external
"Project2.dll"
;
Здесь, как вы уже наверное догадались, мы сообщаем программе названия наших процедур и говорим, что они находятся в dll библиотеке , в моем случае с именем Project2.dll
Теперь для того, что бы вызвать данные процедуры нам необходимо лишь вставить их названия в любое место кода, что мы сейчас и сделаем. Кидаем на форму 2 компонента Button с закладки Standart и создаем на каждой обработчик событий OnClick
OnClick первой кнопки:
Begin
FirstCall;
End;
OnClick второй кнопки:
Begin
DoubleCall; // Имя процедуры, которая находится в dll
End;
Вот и все!
Способ № 2:
Сложнее чем первый, но у него есть свои плюсы, а самое главное, что он идеально подходит для плагинов.
Для применения данного метода, первым делом объявляем несколько глобальных переменных:
Var
LibHandle: HModule; //Ссылка на модуль библиотеки
FirstCall: procedure; stdcall;
//Имена наших процедур лежащих в библиотеке.
DoubleCall: procedure; stdcall;
Затем после ключевого слова implementation напишем процедуру которая будет загружать нашу библиотеку:
Procedure
LoadMyLibrary(FileName: String);
Begin
LibHandle:= LoadLibrary(PWideChar(FileName));
//Загружаем библиотеку!
//
Внимание!
PChar для версий ниже 2009 Delphi
If
LibHandle = 0 then begin
MessageBox(0,"",0,0);
Exit;
End;
FirstCall:= GetProcAddress(LibHandle,"FirstCall
");
//Получаем указатель на объект
//1-ий параметр ссылка на модуль библиотеки
//2-ой параметр имя объекта в dll
DoubleCall:= GetProcAddress(LibHandle,"DoubleCall
");
If
@FirstCall = nil then begin
MessageBox(0,"Невозможно загрузить библиотеку
",0,0);
Exit;
End;
If
@DoubleCall = nil then begin
//Проверяем на наличие этой функции в библиотеке.
MessageBox(0,"Невозможно загрузить библиотеку
",0,0);
Exit;
End; End;
После чего на форме создаем, обработчик событий OnCreate, в котором с помощью только что созданной процедуры мы загрузим нашу библиотеку
Procedure
TForm1.FormCreate(Sender: TObject);
Begin
LoadMyLibrary("Project2.dll
");
End;
Теперь опять же, для того, что бы вызвать необходимые процедуры из нашей библиотеки нам необходимо лишь вставить их названия в любое место кода. Для этого кидаем на форму 2 компонента Button с закладки Standart и создаем на каждой обработчик событий OnClick
OnClick первой кнопки:
Procedure
TForm1.Button1Click(Sender: TObject);
Begin
FirstCall; // Имя процедуры, которая находится в dll
End;
OnClick второй кнопки:
Procedure
TForm1.Button2Click(Sender: TObject);
Begin
DoubleCall; // Имя процедуры, которая находится в dll
End;
Ну и напоследок создаем обработчик событий OnDestroy на форме, в котором выгружаем dll библиотеку из памяти
Procedure
TForm1.FormDestroy(Sender: TObject);
Begin
FreeLibrary(LibHandle);
//Выгружаем библиотеку из памяти.
End;
Вот и все! Второй способ получился довольно громоздкий, но его плюс в уточнении хранящегося объекта в библиотеке.
P.S. Хотите вперед всех остальных посетителей сайта получать свежие Видео уроки, Аудио подкасты, статьи по Delphi.
Участвовать в конкурсах и постепенно вливаться в нашу команду?!
Тогда прямо сейчас подписывайтесь на бесплатную мультимедийную рассылку сайта сайт
Нас уже больше чем 3500 человек!