Поради бързото развитие на технологиите за програмиране, все повече хора се сблъскват с проблема за увеличаване на възможностите на своите програми. Тази статия е посветена на този проблем, а именно програмирането на DLL в Borland Delphi. Освен това, тъй като ще се докоснем до проблемите с използването на DLL файлове, ще се докоснем и до импортирането на функции от DLL файлове на други хора (включително системни, т.е. WinAPI).

DLL приложения

И така, защо са необходими DLL и къде се използват?.. Ето само някои от областите на тяхното приложение:

  • Отделни библиотеки, съдържащ допълнителни функции, полезни за програмистите. Например функции за работа с низове или сложни библиотеки за конвертиране на изображения.
  • Магазини за ресурси. В DLL можете да съхранявате не само програми и функции, но и всякакви ресурси - икони, картинки, низови масиви, менюта и др.
  • Поддържайте библиотеки. Пример са библиотеките на такива добре познати пакети като: DirectX, ICQAPI(API за ICQ), OpenGLи т.н.
  • Части от програмата. Например DLL може да съхранява програмни прозорци (форми) и т.н.
  • Плъгини(Добавки). - Ето къде е истинският обхват за мислите на програмиста! Добавките са добавки към програмата, които разширяват нейните възможности. Например, в тази статия ще разгледаме теорията за създаване на плъгин за вашата собствена програма.
  • Споделен ресурс. DLL( Библиотека с динамични връзки) може да се използва от няколко програми или процеси едновременно (т.нар. споделяне- споделен ресурс)

Кратко описание на функциите и триковете за работа с DLL

И така, какви трикове и функции трябва да използвате, за да работите с DLL? Нека анализираме два метода за импортиране на функции от библиотеката:

1 начин. Свързване на DLL към програма.Това е най-простият и лесен метод за използване на функции, импортирани от DLL. Въпреки това (и трябва да обърнете внимание на това) този метод има много съществен недостатък - ако библиотеката, която програмата използва, не бъде намерена, тогава програмата просто няма да стартира, като дава грешка и съобщава, че DLL ресурсът не е намерен. И библиотеката ще бъде търсена: в текущата директория, в директорията на програмата, в директорията WINDOWS\SYSTEM и т.н.
И така, за начало - общата форма на тази техника:

изпълнение
...
функцияИме на функция(Par1: Par1Type; Par2: Par2Type; ...): ReturnType; stdcall; външен"DLLNAME.DLL" име"Име на функция" индекс FuncIndex;
// или (ако не е функция, а процедура):
процедура ProcedureName(Par1: Par1Type; Par2: Par2Type; ...); stdcall; външен"DLLNAME.DLL" име"име на процедура" индекс ProcIndex;

Тук: Име на функция(или Име на процедура) - името на функцията (или процедурата), която ще се използва във вашата програма;
Пар1, Пар2, ...- имена на функционални или процедурни параметри;
Par1Type, Par2Type, ...- типове параметри на функция или процедура (напр. Цяло число);
тип връщане- тип връщана стойност (само за функция);
stdcall- директива, която трябва точно да съвпада с използваната в самия DLL;
външен "DLLNAME.DLL"- директива, указваща името на външния DLL, от който ще се импортира дадената функция или процедура (в този случай - DLLNAME.DLL);
име "Име на функция" ("Име на процедура")- директива, която указва точното име на функция в самата DLL. Това е незадължителна директива, която ви позволява да използвате функция в програмата, която има име, различно от истинското (което има в библиотеката);
индекс FunctionIndex(ProcedureIndex)- директива, която определя поредния номер на функция или процедура в DLL. Това също е незадължителна директива.

2 начина. Динамично зареждане на DLL.Това е много по-сложен, но и по-елегантен метод. Той е лишен от недостатъка на първия метод. Единственото нещо, което е неприятно, е количеството код, необходим за прилагане на тази техника, а трудността е, че функцията, импортирана от DLL, е достъпна само когато този DLL е зареден и е в паметта ... Можете да прочетете примера по-долу, но засега - кратко описание на функциите на WinAPI, използвани от този метод:

LoadLibrary(име на libфайл: PChar) - зареждане на определената библиотека LibFileName в паметта. При успех функцията връща манипулатор ( THandle) DLL в паметта.
GetProcAddress(Модул: THandle; ProcName: PChar) - чете адреса на експортираната библиотечна функция. При успех функцията връща манипулатор ( TFarProc) функционира в заредената DLL.
безплатна библиотека(LibModule: THandle) - обезсилва LibModule и освобождава паметта, свързана с него. Трябва да се отбележи, че след извикването на тази процедура функциите на тази библиотека вече не са налични.

Практика и примери

Е, сега е време да дадем няколко примера за използването на горните методи и техники:

Сега същото, но по втория начин - с динамично зареждане:

(... Тук идва заглавката на файла и дефиницията на формуляра TForm1 и неговия екземпляр на Form1)

вар
Формуляр1: TForm1;
GetSimpleText: функция(LangRus: Boolean): PChar;
LibHandle: THandle;

процедура Button1Click(Подател: TObject);
започвам
("Ние почистваме" адреса на функцията от "мръсотия")
@GetSimpleText:= нула;
(Опитвам се да заредя библиотеката)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Ако всичко е наред)
ако LibHandle >= 32, тогава започнете
(... тогава се опитваме да получим адреса на функцията в библиотеката)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Ако тук всичко е наред)
ако @GetSimpleText<>тогава нула
(... тогава извикваме тази функция и показваме резултата)
ShowMessage(StrPas(GetSimpleText(True)));
край;
(И не забравяйте да освободите памет и да разтоварите DLL)
FreeLibrary(LibHandle);
край;

ЗАБЕЛЕЖКА : Трябва да се въздържате от използването на типа низ в библиотечните функции, защото има проблеми със "споделянето на паметта" при използването му. Можете да прочетете повече за това (макар и на английски) в текста на празния DLL проект, който Delphi създава (Файл -> Нов -> DLL). Така че е по-добре да използвате PChar и след това да го конвертирате в низ със StrPas, ако е необходимо.

Е, сега нека анализираме директно самия DLL:

Поставяне в DLL ресурси и форми

В DLL можете да поставите не само функции, но и курсори, картини, икони, менюта, текстови редове. Няма да спрем дотук. Ще отбележа само, че за да заредите ресурса, трябва да заредите DLL и след това, след като сте получили неговия дескриптор, заредете самия ресурс със съответната функция (LoadIcon, LoadCursor и т.н.). В този раздел само накратко ще разгледаме разположението на прозорците на приложението (т.е. формуляри в Delphi) в DLL.

За да направите това, трябва да създадете нов DLL и да добавите нов формуляр към него (Файл -> Нов -> DLL и след това Файл -> Нов формуляр). Освен това, ако формулярът е диалогов прозорец (модална форма (bsDialog)), тогава добавете следната функция към DLL (например формулярът се нарича Form1 и неговият клас е TForm1):

Ако трябва да поставите немодална форма в DLL, тогава трябва да направите две функции - отваряне и затваряне на формата. В този случай трябва да принудите DLL да запомни манипулатора на този формуляр.

Създаване на добавки

Тук няма да разглеждаме подробно плъгините, т.к. вече дадените по-горе примери ще ви помогнат лесно да разберете лъвския дял от DLL програмирането. Само да напомня, че плъгинът е допълнение към програмата, което разширява нейните възможности. В същото време самата програма трябва задължително да осигури наличието на такива допълнения и да им позволи да изпълняват предназначението си.

Тоест, например, за да създадете плъгин за графичен редактор, който да извършва преобразуване на изображения, трябва да предоставите поне две функции в плъгина (и съответно да извикате тези функции в програмата) - функция който ще върне името на приставката (и/или неговия тип), за да добави тази приставка към менюто (или лентата с инструменти), плюс основната функция е да изпраща и получава изображение. Тези. първо програмата търси плъгини, след това за всеки намерен извиква своята идентифицираща функция със строго определено име (например GetPluginName) и добавя желания елемент към менюто, след което, ако потребителят е избрал този елемент, извиква втората функция, която предава входното изображение (или името на файл, съдържащ това изображение), а тази функция от своя страна обработва изображението и го връща в нова форма (или име на файл с ново изображение). Това е целият смисъл на плъгина... :-)

Епилог

Тази статия показва основните аспекти на използването и създаването на DLL файлове в Borland Delphi. Ако имате въпроси - изпратете ми ги на E-mail: [имейл защитен], и още по-добре - пишете в конференцията на този сайт, така че другите потребители да могат да видят вашия въпрос и да се опитат да отговорят на него!

Карих Николай. Московска област, Жуковски


Поради бързото развитие на технологиите за програмиране, все повече хора се сблъскват с проблема за увеличаване на възможностите на своите програми. Тази статия е посветена на този проблем, а именно програмирането на DLL в Borland Delphi. Освен това, тъй като ще се докоснем до проблемите с използването на DLL файлове, ще засегнем и импортирането на функции от DLL на други хора (включително системни, т.е. WinAPI).

DLL приложения

И така, защо са необходими DLL и къде се използват?.. Ето само някои от областите на тяхното приложение:

Отделни библиотеки, съдържащи допълнителни функции, полезни за програмисти.

Например функции за работа с низове или сложни библиотеки за конвертиране на изображения.

Магазини за ресурси. В DLL можете да съхранявате не само програми и функции, но и всякакви ресурси - икони, картини, низови масиви, менюта и др.

Поддържайте библиотеки. Пример са библиотеките на такива добре познати пакети като: DirectX, ICQAPI (API за ICQ), OpenGL и др.

Части от програмата. Например DLL може да съхранява програмни прозорци (форми) и т.н.

Плъгини. - Ето къде е истинският обхват за мислите на програмиста! Добавките са добавки към програмата, които разширяват нейните възможности. Например, в тази статия ще разгледаме теорията за създаване на плъгин за вашата собствена програма.

Споделен ресурс. DLL (Dynamic Link Library) може да се използва от няколко програми или процеси едновременно (т.нар. споделяне е споделен ресурс)

Кратко описание на функциите и триковете за работа с DLL

И така, какви трикове и функции трябва да използвате, за да работите с DLL? Нека анализираме два метода за импортиране на функции от библиотеката:

1 начин. Свързване на DLL към програма. Това е най-простият и лесен метод за използване на функции, импортирани от DLL. Въпреки това (и трябва да обърнете внимание на това) този метод има много съществен недостатък - ако библиотеката, която програмата използва, не бъде намерена, тогава програмата просто няма да стартира, като дава грешка и съобщава, че DLL ресурсът не е намерен. И библиотеката ще бъде търсена: в текущата директория, в директорията на програмата, в директорията WINDOWSSYSTEM и т.н.

И така, за начало - общата форма на тази техника:

Внедряване
...
функция Име на функция(Par1: Par1Type; Par2: Par2Type; ...): ReturnType; stdcall; външно "DLLNAME.DLL" име "FunctionName" индекс FuncIndex;
// или (ако не е функция, а процедура):
процедура ProcedureName(Par1: Par1Type; Par2: Par2Type; ...); stdcall; външно "DLLNAME.DLL" име "ProcedureName" индекс ProcIndex;

Тук: FunctionName (или ProcedureName) - името на функцията (или процедурата), която ще се използва във вашата програма;

Par1, Par2, ... - имена на функции или параметри на процедури;
Par1Type, Par2Type, ...- типове параметри на функция или процедура (например Integer);
тип връщане- тип връщана стойност (само за функция);
stdcall- директива, която трябва точно да съвпада с използваната в самия DLL;
външен "DLLNAME.DLL"- директива, указваща името на външния DLL, от който ще се импортира дадената функция или процедура (в случая DLLNAME.DLL);
име "Име на функция"("ProcedureName") е директива, която указва точното име на функция в самата DLL.

Това е незадължителна директива, която ви позволява да използвате функция в програмата, която има име, различно от истинското (което има в библиотеката);
index FunctionIndex (ProcedureIndex) - директива, която указва поредния номер на функция или процедура в DLL. Това също е незадължителна директива.

2 начина. Динамично зареждане на DLL. Това е много по-сложен, но и по-елегантен метод. Той е лишен от недостатъка на първия метод. Единственото нещо, което е неприятно, е количеството код, необходим за прилагане на тази техника, а трудността е, че функцията, импортирана от DLL, е достъпна само когато този DLL е зареден и е в паметта ... Можете да прочетете примера по-долу, но засега - кратко описание на функциите на WinAPI, използвани от този метод:

LoadLibrary(LibFileName: PChar) - зареждане на посочената библиотека LibFileName в паметта. При успех функцията връща манипулатор (THandle) на DLL в паметта.
GetProcAddress(Модул: THandle ; ProcName: PChar) - чете адреса на експортирана библиотечна функция. При успех функцията връща манипулатор (TFarProc) на функцията в заредената DLL.
безплатна библиотека(LibModule: THandle) - Девалифицира LibModule и освобождава свързаната с него памет. Трябва да се отбележи, че след извикването на тази процедура функциите на тази библиотека вече не са налични.

Практика и примери

Е, сега е време да дадем няколко примера за използване на горните методи и техники: Пример 1. Свързване на DLL към програма

Внедряване

(Дефиниране на функция на външна библиотека)

Функция GetSimpleText(LangRus: Boolean): PChar; stdcall; външен "MYDLL.DLL";


започвам
(и го използвай)
ShowMessage(StrPas(GetSimpleText(False)));
(ShowMessage - показва диалогов прозорец с посочения надпис; StrPas - преобразува низ от PChar в низ)
край;

Сега същото, но по втория начин - с динамично зареждане: Пример 2. Динамично зареждане на DLL

(... Тук идва заглавката на файла и дефиницията на формуляра TForm1 и неговия екземпляр на Form1)

вар
Формуляр1: TForm1;
GetSimpleText: функция (LangRus: Boolean): PChar;
LibHandle: THandle;

ProcedureButton1Click(Подател: TObject);
започвам
("Ние почистваме" адреса на функцията от "мръсотия")
@GetSimpleText:= нула;
(Опитвам се да заредя библиотеката)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Ако всичко е наред)
ако LibHandle >= 32, тогава започнете
(... тогава се опитваме да получим адреса на функцията в библиотеката)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Ако тук всичко е наред)
ако @GetSimpleText нула тогава
(... тогава извикваме тази функция и показваме резултата)
ShowMessage(StrPas(GetSimpleText(True)));
край;
(И не забравяйте да освободите памет и да разтоварите DLL)
FreeLibrary(LibHandle);
край;

ЗАБЕЛЕЖКА: Трябва да се въздържате от използването на типа низ в библиотечните функции, защото има проблеми със "споделянето на паметта" при използването му. Можете да прочетете повече за това (макар и на английски) в текста на празния DLL проект, който Delphi създава (Файл -> Нов -> DLL). Така че по-добре използвайте PChar и след това го преобразувайте в низ, ако е необходимо с функция StrPas.

Е, сега нека анализираме директно самия DLL: Пример 3. Източник на проекта MYDLL.DPR
библиотекаmydll;

Използва SysUtils, класове;

(Ние дефинираме функцията като stdcall)
функция GetSimpleText(LangRus: Boolean): PChar; stdcall;
започвам
(В зависимост от LangRus, ние връщаме руска (True) или английска (False) фраза)
ако LangRus тогава
Резултат:= PChar("Здравей свят!")
друго
Резултат:= PChar("Здравей, свят!");
край;

(Директивата за експортиране указва кои функции ще бъдат експортирани от този DLL)
експортира GetSimpleText;

Започнете
край.

Поставяне в DLL ресурси и форми

В DLL можете да поставите не само функции, но и курсори, картини, икони, менюта, текстови редове. Няма да спрем дотук. Ще отбележа само, че за да заредите ресурса, трябва да заредите DLL и след това, след като сте получили неговия дескриптор, заредете самия ресурс със съответната функция (LoadIcon, LoadCursor и т.н.). В този раздел само накратко ще разгледаме разположението на прозорците на приложението (т.е. формуляри в Delphi) в DLL.

За да направите това, трябва да създадете нов DLL и да добавите нов формуляр към него (Файл -> Нов -> DLL и след това Файл -> Нов формуляр). Освен това, ако формулярът е диалогов прозорец (модална форма (bsDialog)), тогава добавете следната функция към DLL (например формулярът се нарича Form1 и неговият клас е TForm1): Пример 4. Поставяне на формуляра в DLL
функция ShowMyDialog(Msg: PChar): Boolean; stdcall;

...
експортира ShowMyDialog;

Функция ShowMyDialog(Msg: PChar): Boolean;
започвам
(Създаване на екземпляр на Form1 формуляр TForm1)
Form1:= TForm1.Create(Application);
(В Label1 показваме Msg)
Form1.Label1.Caption:= StrPas(Msg);
(Връща True само ако е натиснато OK (ModalResult = mrOk))
Резултат:= (Form1.ShowModal = mrOk);
(Освобождаване на паметта)
Form1.Free;
край;

Ако трябва да поставите немодална форма в DLL, тогава трябва да направите две функции - отваряне и затваряне на формата. В този случай трябва да принудите DLL да запомни манипулатора на този формуляр.

Създаване на добавки

Тук няма да разглеждаме подробно плъгините, т.к. вече дадените по-горе примери ще ви помогнат лесно да разберете лъвския дял от DLL програмирането. Само да напомня, че плъгинът е допълнение към програмата, което разширява нейните възможности. В същото време самата програма трябва задължително да осигури наличието на такива допълнения и да им позволи да изпълняват предназначението си.

Тоест, например, за да създадете плъгин за графичен редактор, който да извършва преобразуване на изображения, трябва да предоставите поне две функции в плъгина (и съответно да извикате тези функции в програмата) - функция който ще върне името на приставката (и/или неговия тип), за да добави тази приставка към менюто (или лентата с инструменти), плюс основната функция е да изпраща и получава изображение. Тези. първо програмата търси плъгини, след това за всеки намерен извиква своята идентифицираща функция със строго определено име (например GetPluginName) и добавя желания елемент към менюто, след което, ако потребителят е избрал този елемент, извиква втората функция, която предава входното изображение (или името на файл, съдържащ това изображение), а тази функция от своя страна обработва изображението и го връща в нова форма (или име на файл с ново изображение). Това е целият смисъл на плъгина... :-)

Използване на DLL в Delphi
  • Концепцията за DLL
  • Създаване на DLL в Delphi (експортиране)
  • Използване на DLL в Delphi (импортиране)
  • DLL файлове, които използват VCL обекти за работа с данни
  • Изключения в DLL
  • Концепцията за DLL

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

Program1 Program2: : MyFunc(:) MyFunc(:) : : функционален код MyFunc код на функция MyFunc код на други функции код на други функции

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

Но каква е разликата между Dynamic Link Library (DLL) и обикновените приложения? За да се разбере това, е необходимо да се изяснят концепциите за задача (task), екземпляр (копие) на приложение (instance) и модул (module).

Когато изпълнява множество екземпляри на едно приложение, Windows зарежда в RAM само едно копие на кода и ресурсите - модула на приложението, създавайки няколко отделни сегмента от данни, стек и опашка от съобщения (вижте фиг. 3), всеки набор от които е задача, в разбирането на Windows. Копието на приложението е контекстът, в който се изпълнява модулът на приложението.

DLL - библиотеката също е модул. Той се намира в паметта в едно копие и съдържа кодов сегмент и ресурси, както и сегмент с данни (вижте Фигура 4).

DLL е библиотека, за разлика от приложението, тя няма нито стек, нито опашка за съобщения. Функциите, поставени в DLL, се изпълняват в контекста на извикващото приложение, използвайки неговия стек. Но същите тези функции използват сегмента от данни на библиотеката, а не копието на приложението.

Благодарение на тази организация на DLL се постига спестяване на памет поради факта, че всички работещи приложения използват един DLL модул, без да включват определени стандартни функции в своите модули.

Често под формата на DLL се създават отделни набори от функции, обединени според една или друга логическа характеристика, подобно на това как концептуално се планират модулите (в смисъл на единица) в Pascal. Разликата е, че функциите от модулите на Pascal се свързват статично - на етапа на свързване, а функциите от DLL се свързват динамично, тоест по време на изпълнение.

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

За програмиране на DLL, Delphi предоставя редица ключови думи и синтактични правила. Основното - DLL в Delphi е същият проект като програма.

Помислете за DLL шаблон:


Името на файла на проекта за такъв шаблон трябва да бъде MYDLL.DPR.

За съжаление само програмният проект се генерира автоматично в Delphi IDE, така че трябва да подготвите DLL проекта ръчно. Delphi 2.0 премахна това неудобство.

Подобно на програма, DLL има раздел за използване. Частта за инициализация не е задължителна. Разделът за експорти изброява функциите, които трябва да бъдат достъпни от външни приложения.

Експортирането на функции (и процедури) може да се извърши по няколко начина:

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

В зависимост от това се използва различен синтаксис:


Тъй като Windows има концепцията за „резидентни функции“ на DLL, тоест тези функции, които са в паметта през целия живот на DLL в паметта, Delphi има инструменти за организиране и такъв вид експортиране:


тогава индексирането на експортираните функции ще се извърши автоматично от Delphi и такова експортиране ще се счита за експортиране по името, което съвпада с името на функцията. След това декларацията на импортираната функция в приложението трябва да съвпада с името на декларацията на функцията в DLL. Що се отнася до директивите, които вече са наложени върху импортираните функции, ще говорим за това по-долу.

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

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

За примерите, показани по-горе, вашата програма трябва да декларира функциите, импортирани от DLL, така:


Този метод се нарича статичен импорт.

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

За да разрешите този вид проблем, можете да получите директен достъп до Windows API, като използвате така нареченото динамично импортиране:


използва WinTypes, WinProcs, ... ; Тип TMyProc = процедура ; варДръжка: THandle; MyImportProc: TMyProc; започвам Handle:= LoadLibrary("MYDLL"); акоДръжка >= 32 тогава (ако започвам@MyImportProc:= GetProcAddress(Манипулатор, "MYEXPORTPROC"); ако MyImportProc нула тогава ... (използване на импортирана процедура) край; Безплатна библиотека (ръкохватка); край;

Синтаксични диаграми на декларации за експортиране/импортиране, заместване на изходна точка на DLL и други примери могат да бъдат намерени в OnLine Help Delphi, езиковото ръководство за Object Pascal, включено в Borland RAD Pack за Delphi, и например в книгата „Научете се Делфи за 21 дни".

Освен кода, генериран от компилатора (сега той е по-оптимизиран), тогава всички синтаксисни правила остават същите като в 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. Можем да препоръчаме връщане на резултата от импортираната функция като низ или число и, ако е необходимо, повторно хвърляне на изключението в програмата.


функция MyFunc: низ; започвам опитвам (действителен функционален код) с изключение НаРезултат: Изключение направиРезултат:=Формат(DllErrorViewingTable, ) другоРезултат:= Format(DllErrorViewingTable, ["Неизвестна грешка"]); край; край;

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


StrResult:= MyFunc; ако StrResult "" тогава повишавам Exception.Create(StrResult);

статия Използване на DLL в DelphiРазделът DLL Filesystem and PlugIns може да бъде полезен за разработчиците на Delphi и FreePascal.

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

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

DLLinstance: THandle;

HelloWorld:THelloWorld;

започвам

(зареди DLL)

(вземете указател)

(извикване на процедурата за изпълнение)

HelloWorld(Self);

(разтоварване на DLL от RAM)

FreeLibrary(DLLInstance) ;

край ;

Както бе споменато по-горе, един от недостатъците на статичното зареждане на DLL е невъзможността
продължете приложението при липса на една или повече библиотеки. В случай на динамичен
зареждане, имате възможност програмно да обработвате такива ситуации и да предотвратявате програмата от
падна от само себе си. Според стойностите, върнати от функциите LoadLibrary и GetProcAddress, можете
определи дали зареждането на библиотеката е било успешно и дали процедурата, изисквана от приложението, е намерена в нея.
Кодът по-долу демонстрира това.

процедура TForm1.DynamicLoadBtnClick (Подател: TObject) ;

Тип

THelloWorld = процедура (AForm: TForm) ;

DLLinstance: THandle;

HelloWorld:THelloWorld;

започвам

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

ако DLLinstance = 0, тогава стартирайте

MessageDlg( „Не може да се зареди DLL“, mtError, [ mbOK] , 0 ) ;

изход;

край ;

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

ако @HelloWorld нула тогава

HelloWorld (себе си)

друго

MessageDlg( „Исканата процедура не е намерена!“, mtError, [ mbOK] , 0 ) ;

FreeLibrary(DLLInstance) ;

край ;

В DLL можете да съхранявате не само код, но и формуляри.
Освен това създаването и поставянето на формуляри в динамична библиотека не се различава много от работата
с формуляри в обикновен проект. Първо, ще разгледаме как можете да напишете библиотека,
съдържащ формуляри и след това ще говорим за използването на MDI технология в DLL.
Ще демонстрирам разработката на DLL, съдържащ формуляр, с пример.
И така, първо, нека създадем нов проект за динамична библиотека.
За да направите това, изберете елемента от менюто File|New и след това щракнете двукратно върху иконата на DLL.
След това ще видите нещо като следния код:

Запазете получения проект. Нека го наречем DllForms.dpr.
Сега трябва да създадем нова форма. Това може да стане по различни начини.
Например, като изберете елемента от менюто Файл|Нов формуляр. Добавете някои компоненти към формата.
Нека назовем формата DllForm и да запазим получената единица като DllFormUnit.pas.
Нека се върнем към основния модул на проекта и поставим в него функцията ShowForm, чиято задача ще включва
създаване на формуляр и показването му на екрана. Използвайте кода по-долу за това.

Форма: TDLLForm;

започвам

Резултат:= Form.ShowModal ;

Form.Free ;

край ;

Обръщам внимание на факта, че за да може проектът да бъде компилиран без грешки, е необходимо да добавите модул Forms към секцията uses.
Ние експортираме нашата функция, използвайки ключовата дума exports:
износ
ShowForm;
Компилираме проекта и получаваме файла dllforms.dll. Тези прости стъпки са всичко
какво трябва да направите, за да съберете Обърнете внимание, че функцията ShowForm е декларирана с помощта на ключовата дума stdcall.
Той сигнализира на компилатора да използва конвенцията, когато експортира функция
чрез стандартна конвенция за повикване. Експортирането на функция по този начин създава
възможността да се използва разработената DLL не само в приложения, създадени в Delphi.
Конвенциите за извикване определят как се предават аргументите, когато се извиква функция.
Има пет основни конвенции: stdcall, cdecl, pascal, register и safecall.
Можете да научите повече за това, като разгледате раздела „Конвенции за извикване“ на помощния файл на Delphi.
Също така имайте предвид, че стойността, върната от функцията ShowForm,
съответства на стойността ShowModal. По този начин можете да предадете някаква информация
относно състоянието на формуляра към извикващото приложение.
По-долу има два списъка, първият от които съдържа пълния код на файла
DLL проект (модулът с формуляра не е показан тук), а вторият е модулът на извикващото приложение,
който използва библиотеката, която току-що разработихме.

библиотека DllForms;

използва

DllFormUnit в "DllFormUnit.pas" (DllForm) ;

($R*.RES)

функция ShowForm: Integer ; stdcall;

Форма: TDLLForm;

започвам

Form:= TDLLForm.Create(Application) ;

Резултат:= Form.ShowModal ;

Form.Free ;

край ;

започвам

край.


единица TestAppUnit;

интерфейс

използва

Windows, съобщения, SysUtils, класове, графики,

Контроли, Формуляри, Диалогови прозорци, StdCtrls;

Тип

TForm1 = клас (TForm)

Бутон1: TButton;

процедура Button1Click(Sender: TObject ) ;

частен

(Лични декларации)

публичен

(Публични декларации)

край ;

Формуляр1: TForm1;

функция ShowForm: Integer ; stdcall;

Външен "dllforms.dll" ;

изпълнение

($R *.DFM)

процедура TForm1.Button1Click (Подател: TObject) ;

започвам

край ;

край.

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

процедура ShowMDIChild(MainApp: TApplication) ;

Дете: TMDIChild;

започвам

ако не е присвоено (DllApp), тогава започнете

DllApp:= Приложение;

Приложение:= MainApp;

край ;

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

Дете.Шоу ;

край ;

Всичко, което трябва да направим сега, е да осигурим връщане на стойността на обекта Application.
до първоначалното състояние. Правим това с помощта на процедурата MyDllProc:

процедура MyDLLProc(Причина: Цяло число) ;

започвам

ако Reason = DLL_PROCESS_DETACH тогава

(DLL се разтоварва. Възстановява се стойността на указателя на приложението)

ако е присвоено (DllApp), тогава

Приложение:= DllApp;

край ;

вместо заключение.
Използването на библиотеки с динамични връзки не е толкова трудно, колкото може да изглежда на пръв поглед.

Използването на библиотеки с динамични връзки не е толкова трудно, колкото може да изглежда на пръв поглед.
DLL предоставят най-широки възможности за оптимизиране на производителността на приложенията,
както и работата на самите програмисти. Използвайте DLL и може би животът ви ще бъде по-лесен!
http://subscribe.ru/
Електронна поща: [имейл защитен]Търсене
към APORT на Subscribe.Ru

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

Бих искал да дам извадка от "Уроците по Delphi" за структурата на DLL в паметта:

DLL е библиотека, за разлика от приложението, тя няма нито стек, нито опашка за съобщения. Функциите, поставени в DLL, се изпълняват в контекста на извикващото приложение, използвайки неговия стек. Но същите тези функции използват сегмента от данни на библиотеката, а не копието на приложението. Благодарение на тази организация на DLL се постига спестяване на памет поради факта, че всички работещи приложения използват един DLL модул, без да включват определени стандартни функции в своите модули. Често под формата на DLL се създават отделни набори от функции, обединени според една или друга логическа характеристика, подобно на това как концептуално се планират модулите (в смисъл на единица) в Pascal. Разликата е, че функциите от модулите на Pascal се свързват статично - на етапа на свързване, а функциите от DLL се свързват динамично, тоест по време на изпълнение.

Създаване на DLL

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

За всяка процедура или функция можете да посочите нейния номер с помощта на директивата Index. Ако номерът липсва, компилаторът ще извърши автоматично индексиране. Вместо номер на процедура можете да използвате уникално име, което се указва с помощта на директивата за име. Ако нито името на функцията, нито нейният номер са посочени, тогава Delphi ще възприеме това като експортиране по име, което ще съответства на името на функцията.

Библиотеката може също да съдържа код за инициализация, който ще бъде изпълнен, когато се зареди. Поставя се между началото и края. Ето общата структура на DLL:

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

Ще дам примери за описанието на експортираните функции в секцията за експортиране:

Експортира функция1 индекс 2; Име на функция 2 "My_sqr"; функция3;

Както може би се досещате, името на функцията може да не съвпада с името за експорт!!!

Използване на DLL

Модул, който трябва да използва процедури и функции от DLL, трябва да използва външната директива. Има два начина за използване на DLL (динамичен и статичен). В първия случай приложението, което извиква функцията от DLL, знае името на библиотеката и нейната входна точка и се предполага, че библиотеката не се променя. Във втория случай, преди да използвате DLL, трябва да се уверите, че необходимата библиотека съществува и че съдържа необходимата подпрограма.

Можете да импортирате подпрограма по нейното име и номер. Търсенето на подпрограма по номер е по-бързо, но винаги е удобно.

Следват примери за импортиране на функции от нашата First_DLL, която беше разгледана в примера:

( импортиране по зададено име ) Функция ImportByName; външно "First_DLL" име "My_sqr"; ( импортиране по индекс ) Функция ImportByOrdinal; външен "First_DLL" индекс 2; (импортиране с оригинално име) Функция Function3; външен "First_DLL";

Разгледахме статичния метод за използване на DLL. Но в някои случаи не е известно предварително коя библиотека ще е необходима, така че трябва да използвате динамичния метод:

Използва WinTypes, WinProcs, ... ; тип TMyProc = процедура; var Handle: THandle; MyImportProc: TMyProc; begin Handle:=LoadLibrary("FirstDLL"); if Handle>=32 then ( if<=32 - ошибка! } begin @MyImportProc:=GetProcAddress(Handle,"My_sqr"); if MyImportProc<>nil then ...... (тук използваме получената функция) end; Безплатна библиотека (ръкохватка); край;

Но според мен всичко написано тук не е много ясно и искам реални завършени примери. Винаги съм бил разочарован, когато в статиите имаше малко примери, но само една теория, затова предлагам на вашето внимание прост пример за използване на DLL.

Кликнете върху менюто File -> New и изберете DLL. Запазете готовия шаблон, както е предложено, под името Project1.dpr.

По-долу е пълният му код:

библиотека проект1; използва SysUtils,Classes; Функция Function1(x,y:integer):integer; износ; bginresult:=x+y; край; Функция Function2(x,y:real):real; износ; vart:реално; начало t:=exp(y*ln(x)); резултат:=t; край; експортира функция1 индекс 1, име на функция2 "My_sqr"; начало край.

Тук има две функции, първата изчислява сумата от две числа и се експортира по число, а втората изчислява x на степен y и се експортира по име.

Сега нека създадем нов проект и да го запазим под друго име, например DemoDLL. Нека поставим два бутона върху формата, като щракването върху първия ще извика първата процедура, а щракването върху втория ще извика втората. Ето пълния код за този демо проект:

демонстрация на единица; интерфейсът използва Windows, Съобщения, SysUtils, Класове, Графики, Контроли, Формуляри, Диалози, StdCtrls; тип TForm1 = class(TForm) Button1: TButton; Бутон2: TButton; процедура Button1Click(Подател: TObject); процедура Button2Click(Подател: TObject); частни ( Частни декларации ) публични ( Публични декларации ) край; var Form1: TForm1; изпълнение ($R *.DFM) функция ImportSumm(x,y:integer):integer; външен "Проект1" индекс 1; //импортиране по номер функция ImportSqr(x,y:real):real; външен "Проект1" име "My_sqr"; //импортиране по име procedure TForm1.Button1Click(Sender: TObject); vart:реално; begin //Намерете колко две на трета степен ще бъде t:=ImportSqr(2,3); Покажи съобщение (FloatTostr(t)); край; процедура TForm1.Button2Click(Подател: TObject); vart:цяло число; begin //Намерете колко ще бъде 10+10 t:=ImportSumm(10,10); Покажи съобщение(IntTostr(t)); край; край.

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