Delphi как подключить dll библиотеку к проекту
В этой статье мы рассмотрим одну очень интересную и полезную вещицу , а именно применение библиотек динамической компоновки, для создания своих программ. Если развернуто расписывать эту фишку, то в результате получится весьма объемный и сложный для понимания материал, да и к чему это делать если все это давным давно уже сделано профессионалами? Нам для ознакомления достаточно понять, что упомянутые выше библиотеки, представляют собой файлы с расширением .dll, содержащие написанные нами процедуры и функции. Возникает резонный вопрос, - "А для чего все это нужно и какая от этого польза?"). Надеюсь, что разбирая ответ на него, взамен мы получим понимание и умение применять .dll библиотеки в своих программах.
Итак, представим себе что мы написали какую нибудь объемную программулину, с кучей различных процедур, функций, описаний и прочих элементов. Как правило когда появляется необходимость спустя некоторое время (скажем через месяц) внести в программу какие либо изменения, то в во всем написанном вами же коде, сам черт ногу сломит, и это при том, что каждая процедура будет снабжена подробнейшим комментарием). Да и сам размер как исполняемого файла, так и области памяти куда программа будет загружаться помимо прочего, напрямую зависит от объема общего листинга программы.
Как же поступить, чтобы оптимизировать создаваемое нами ПО.
- Вот тут нам на помощь и приходят динамические библиотеки .dll .
То-есть что по сути происходит? Мы создаем в каталоге с программой файл, или ряд файлов с расширением .dll, загружаем их всеми нашими процедурами, функциями ну и как правило там же и описываем их. А далее по мере необходимости, мы указываем нашей программе, из какого файла .dll нам нужно вызвать нужную нам процедуру или функцию))). То-есть выделение для этого памяти системой, происходит только в момент вызова))). Ну и когда объемные описания вынесены из общего листинга проекта, в нем гораздо проще разобраться это во первых, и при компиляции получается более компактный файл это - во вторых))). В третьих если в последствии нужно сделать апдейт, то гораздо удобнее заменить дэлэлку в каталоге с программкой, чем заменять программу целиком).
Рассмотреть как все это происходит, я предлагаю на сквозном примере, сложения двух чисел, где собственно процедура самого сложения будет находиться в файле .dll.
2) Размещаем на форме 3 компонента Edit , 2 компонента Label , 1 компонент Button, и настраиваем примерно как у меня, затем компилируем и сохраняем;
3) Далее заходим в пункт меню File -> New -> Other -> Dynamic-linc Library , жмем Ок и сохраняем все в папке с нашим проектом, только изменяем имя сохраняемого на Project2 .
4) В открывшемся листинге, текст между разделами library Project2 и uses, можно удалить, после значения , создаем нашу функцию сложения и описываем ее:
Function summ(a,b:Integer):Integer; StdCall; < Называем ее именем summ, тип переменных Integer, выходной параметр тоже Integer,
StdCall - говорит о стандартном способе вызова, то-есть о совместимости с Windows API, если его не указать то сторонние разработчики ПО будут испытывать сложности с подключением к нашей библиотеке, если мы не планируем, чтобы кроме нас библиотеку кто то использовал еще, то можно его и
Что такое DLL - знает, как минимум, большинство пользователей PC, тем более программисты, к которым Вы, скорее всего и относитесь, раз читаете эту статью. В этой статье я постараюсь пробежаться по всем общим вопросам, касающимся DLL.
Что конкретно мы рассмотрим:
- Как обычно, из области "Hello World", мы создадим свою первую DLL.
- Научимся пользоваться функциями этой DLL из своих программ.
- Научимся просматривать функции, которые экспортирует определенная DLL.
- Может, что нибудь еще.
Процесс создания DLL
- Запускаем Delphi (Я использую Delphi 6).
- Далее: File -> New ->Other
На закладке New дважды щелкаем по объекту DLL Wizard. Откроется новый проект. Сохраните его, например, с именем MyFirstDLL.
Чистый модуль имеет примерно такое содержание:
Теперь напишем всего лишь одну функцию, которая вызовет ShowMessage() из модуля Dialogs. Следовательно, перед началом оформления процедуры допишем в раздел Uses модуль Dialogs. Что, примерно, должно у вас получится:
Как видите, тут нет ничего очень сложного. Единственное скажу, что можно вызывать функции как по имени, так и по индексу (номеру), для этого надо писать так:
Использование функций DLL
Первый шаг сделан, дело за малым. Как нам использовать эту функцию?
Есть, как минимум, два способа загрузки:
Создаем новый проект, бросаем на форму одну кнопку и по событию OnClick этой кнопки и пишем следующее:
Но это еще не все! В разделе implementation проекта запишите:
Теперь рассмотрим способ с динамической загрузкой. Для этого метода используют функцию LoadLibrary( ), а в конце, для выгрузки - FreeLibrary( ).
Посмотрите на примере:
После успешной загрузки 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, в нём мы будем показывать имена функций.
Вот весь проект:
Если вы захотите сами разобраться в коде и у вас что-то не будет получаться, то на нашем форуме вам обязательно помогут, заходите!
Прицепляем наш Viewer ко всем DLL
У нас есть готовая DLL с функцией, есть просмотрщик функций. Осталось добавить некой функциональности для удобства дальнейшей работы. Давайте сделаем это. В проводнике открываем любую папку. Идем в Сервис -> Свойства папки. Переходим на закладку "Типы файлов". В списке ищем формат DLL. Если такого нет, то жмем кнопку "Создать" и в поле "Расширение" пишем - DLL. Жмем ОК. Находим созданный нами тип - DLL. Выделяем его и жмем "Дополнительно". Далее "Создать", в поле "Действии" пишем то, что будет отображаться в контекстном меню, например DLL Viewer. Через обзор ищем нашу программку.
Теперь при клике правой кнопкой мыши по файлу формата DLL в меню будет наш DLL Viewer. Выбираем его и смотрим все функции!
В связи с бурным развитием технологий программирования, все больше людей сталкиваются с проблемой наращивания возможностей своих программ. Данная статья посвящена именно этому вопросу, а именно - программирование 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 и освобождает связанную с ним память. Следует заметить, что после вызова этой процедуры функции данной библиотеки больше недоступны.
Практика и примеры
Ну а теперь пора привести пару примеров использования вышеперечисленных методов и приемов:
Пример 1. Привязка DLL к программе
function GetSimpleText(LangRus: Boolean): PChar; stdcall; external 'MYDLL.DLL';
Теперь то же самое, но вторым способом - с динамической загрузкой:
Пример 2. Динамическая загрузка DLL
var
Form1: TForm1;
GetSimpleText: function (LangRus: Boolean): PChar;
LibHandle: THandle;
ПРИМЕЧАНИЕ : Следует воздерживаться от использования типа string в библиотечных функциях, т.к. при его использовании существуют проблемы с "разделением памяти". Подробней об этом можно прочитать (правда, на английском) в тексте пустого проекта DLL, который создает Delphi (File -> New -> DLL). Так что лучше используйте PChar, а затем при необходимости конвертируйте его в string функцией StrPas.
Ну а теперь разберем непосредственно саму библиотеку DLL:
Пример 3. Исходник проекта MYDLL.DPR
uses SysUtils, Classes;
Размещение в DLL ресурсов и форм
В DLL можно размещать не только функции, но и курсоры, рисунки, иконки, меню, текстовые строки. На этом мы останавливаться не будем. Замечу лишь, что для загрузки ресурса нужно загрузить DLL, а затем, получив ее дескриптор, - загружать сам ресурс соотвествующей функцией (LoadIcon, LoadCursor, и т.д.). В этом разделе мы лишь немного затронем размещение в библиотеках DLL окон приложения (т.е. форм в Дельфи).
Для этого нужно создать новую DLL и добавить в нее новую форму (File -> New -> DLL, а затем - File -> New Form). Далее, если форма представляет собой диалоговое окно (модальную форму (bsDialog)), то добавляем в DLL следующую функцию (допустим, форма называется Form1, а ее класс - TForm1):
Пример 4. Размещение формы в DLL
Если же нужно разместить в DLL немодальную форму, то необходимо сделать две функции - открытия и закрытия формы. При этом нужно заставить DLL запомнить дескриптор этой формы.
Создание плагинов
Здесь мы не будем подробно рассматривать плагины, т.к. уже приведенные выше примеры помогут Вам легко разобраться в львиной части программирования DLL. Напомню лишь, что плагин - дополнение к программе, расширяющее ее возможности. При этом сама программа обязательно должна предусматривать наличие таких дополнений и позволять им выполнять свое предназначение.
Т.е., например, чтобы создать плагин к графическому редактору, который бы выполнял преобразование изображений, Вам нужно предусмотреть как минимум две функции в плагине (и, соответственно, вызвать эти функции в программе) - функция, которая бы возвращала имя плагина (и/или его тип), чтобы добавить этот плагин в меню (или в тулбар), плюс главная функция - передачи и приема изображения. Т.е. сначала программа ищет плагины, потом для каждого найденного вызывает его опозновательную функцию со строго определенным именем (например, GetPluginName) и добавляет нужный пункт в меню, затем, если пользователь выбрал этот пункт - вызывает вторую функцию, которой передает входное изображение (либо имя файла, содержащего это изображение), а эта функция, в свою очередь, обрабатывает изображение и возвращает его в новом виде (или имя файла с новым изображением). Вот и вся сущность плагина. :-)
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; // Внимание ! Не забудьте указать эти модули,
// иначе код работать не будет
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 первой кнопки:
Procedure TForm1.Button1Click(Sender: TObject);
Begin
FirstCall; // Имя процедуры, которая находится в dll
End;
OnClick второй кнопки:
Procedure TForm1.Button2Click(Sender: TObject);
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;
Вот и все ! Второй способ получился довольно громоздкий, но его плюс в уточнении хранящегося объекта в библиотеке.
Читайте также:
- Скорость передачи данных составляет 512000 бит с необходимо передать файл размером 125 мбайт
- Usb to gpio adapter для восстановления батареи своими руками
- Как разблокировать контроллер батареи ноутбука
- Два слота памяти для сохранения настроек температуры и напора воды
- Как сделать поиск по странице в гугл хром