Vzhledem k rychlému rozvoji programovacích technologií se stále více lidí potýká s problémem zvýšení schopností svých programů. Tento článek je věnován této problematice, konkrétně programování DLL v Borland Delphi. Navíc, protože se dotkneme problematiky používání DLL, dotkneme se i importu funkcí z cizích DLL (včetně systémových, tedy WinAPI).

DLL aplikace

Proč jsou tedy knihovny DLL potřebné a kde se používají?. Zde je jen několik oblastí jejich použití:

  • Samostatné knihovny, obsahující další funkce užitečné pro programátory. Například funkce pro práci s řetězci, nebo složité knihovny pro převod obrázků.
  • Obchody se zdroji. V DLL můžete ukládat nejen programy a funkce, ale také všechny druhy zdrojů - ikony, obrázky, pole řetězců, nabídky atd.
  • Podpora knihoven. Příkladem jsou knihovny takových známých balíčků, jako jsou: DirectX, ICQAPI(API pro ICQ), OpenGL atd.
  • Části programu. DLL může například ukládat okna programu (formuláře) atd.
  • Pluginy(Pluginy). - Tam je skutečný prostor pro myšlenky programátora! Pluginy jsou doplňky k programu, které rozšiřují jeho možnosti. V tomto článku se například podíváme na teorii tvorby pluginu pro vlastní program.
  • Sdílený zdroj. DLL( Knihovna dynamických odkazů) může být používáno několika programy nebo procesy najednou (tzv. sdílení- sdílený zdroj)

Stručný popis funkcí a triků pro práci s DLL

Jaké triky a funkce tedy musíte použít pro práci s DLL? Pojďme analyzovat dvě metody pro import funkcí z knihovny:

1 způsob. Vazba DLL k programu. Toto je nejjednodušší a nejjednodušší způsob použití funkcí importovaných z knihovny DLL. Tato metoda má však (a měli byste tomu věnovat pozornost) velmi významnou nevýhodu - pokud není nalezena knihovna, kterou program používá, program se jednoduše nespustí, zobrazí chybu a hlásí, že zdroj DLL nebyl nalezen. A knihovna bude prohledána: v aktuálním adresáři, v adresáři programu, v adresáři WINDOWS\SYSTEM atd.
Takže pro začátek - obecná forma této techniky:

implementace
...
funkce NázevFunkce(Par1: Par1Type; Par2: Par2Type; ...): ReturnType; stdcall; externí"DLLNAME.DLL" název"Název funkce" index FuncIndex;
// nebo (pokud to není funkce, ale procedura):
postup ProcedureName(Par1: Par1Type; Par2: Par2Type; ...); stdcall; externí"DLLNAME.DLL" název"název procedury" index ProcIndex;

Tady: Název funkce(nebo Název procedury) - název funkce (nebo procedury), která bude použita ve vašem programu;
Par1, Par2,...- názvy parametrů funkce nebo procedury;
Par1Type, Par2Type, ...- typy parametrů funkce nebo procedury (např. Celé číslo);
návratový typ- typ návratové hodnoty (pouze pro funkci);
stdcall- direktiva, která se musí přesně shodovat s direktivou použitou v samotné DLL;
externí "DLLNAME.DLL"- direktiva určující název externí DLL, ze které bude daná funkce nebo procedura importována (v tomto případě - DLLNAME.DLL);
název "FunctionName" ("ProcedureName")- direktiva, která specifikuje přesný název funkce v samotné DLL. Toto je volitelná direktiva, která vám umožňuje používat v programu funkci, která má název odlišný od skutečného (který má v knihovně);
index FunctionIndex(ProcedureIndex)- direktiva, která určuje pořadové číslo funkce nebo procedury v DLL. Toto je také nepovinná směrnice.

2 způsobem. Dynamické načítání DLL. Jedná se o mnohem složitější, ale také elegantnější metodu. Postrádá nevýhodu prvního způsobu. Jediná věc, která je nepříjemná, je množství kódu potřebného k implementaci této techniky a problém je v tom, že funkce importovaná z DLL je dostupná pouze tehdy, když je tato DLL načtena a je v paměti ... Můžete si přečíst příklad níže, ale prozatím - krátký popis funkcí WinAPI používaných touto metodou:

LoadLibrary(libfilename: PChar) - načtení zadané knihovny LibFileName do paměti. Při úspěchu funkce vrátí handle ( Thandle) DLL v paměti.
GetProcAddress(Modul: Thandle; ProcName: PChar) - přečte adresu exportované knihovní funkce. Při úspěchu funkce vrátí handle ( TFarProc) funkce v načtené DLL.
volná knihovna(LibModul: Thandle) - zruší platnost modulu LibModule a uvolní paměť s ním spojenou. Je třeba poznamenat, že po zavolání této procedury již nejsou funkce této knihovny dostupné.

Praxe a příklady

Nyní je čas uvést několik příkladů použití výše uvedených metod a technik:

Nyní to samé, ale druhým způsobem - s dynamickým zatížením:

(... Zde je záhlaví souboru a definice formuláře TForm1 a jeho instance Form1)

var
Form1: TForm1;
GetSimpleText: funkce(LangRus: Boolean): PChar;
LibHandle: THandle;

procedure Button1Click(Sender: TObject);
začít
("Vyčistíme" adresu funkce od "nečistoty")
@GetSimpleText:= nula;
(Pokouším se načíst knihovnu)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Pokud je vše v pořádku)
pokud LibHandle >= 32, pak začněte
(...pak se snažíme získat adresu funkce v knihovně)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Pokud je zde vše v pořádku)
if @GetSimpleText<>pak nula
(... pak tuto funkci zavoláme a ukážeme výsledek)
ShowMessage(StrPas(GetSimpleText(True)));
konec;
(A nezapomeňte uvolnit paměť a uvolnit DLL)
FreeLibrary(LibHandle);
konec;

POZNÁMKA : Měli byste se zdržet používání typu řetězec ve funkcích knihovny, protože při jeho používání dochází k problémům se „sdílením paměti“. Více si o tom můžete přečíst (i když v angličtině) v textu prázdného projektu DLL, který Delphi vytváří (Soubor -> Nový -> DLL). Je tedy lepší použít PChar a ten pak v případě potřeby převést na řetězec pomocí StrPas.

Nyní pojďme analyzovat samotnou DLL přímo:

Umístění v DLL zdrojích a formulářích

Do DLL můžete umístit nejen funkce, ale také kurzory, obrázky, ikony, nabídky, textové řádky. U toho se nezastavíme. Uvedu pouze, že pro načtení zdroje je třeba načíst DLL a poté, co obdržíme jeho deskriptor, načíst samotný zdroj s příslušnou funkcí (LoadIcon, LoadCursor atd.). V této části se jen krátce dotkneme umístění oken aplikací (tj. formulářů v Delphi) v knihovnách DLL.

Chcete-li to provést, musíte vytvořit novou knihovnu DLL a přidat do ní nový formulář (Soubor -> Nový -> DLL a poté Soubor -> Nový formulář). Dále, pokud je formulář dialogové okno (modální formulář (bsDialog)), přidejte do knihovny DLL následující funkci (například formulář se nazývá Form1 a jeho třída je TForm1):

Pokud potřebujete umístit nemodální formulář do DLL, musíte provést dvě funkce - otevření a zavření formuláře. V tomto případě musíte vynutit DLL, aby si pamatovala popisovač tohoto formuláře.

Vytváření pluginů

Zde nebudeme podrobně zvažovat pluginy, protože. výše uvedené příklady vám pomohou snadno pochopit lví podíl programování DLL. Jen připomenu, že plug-in je doplněk k programu, který rozšiřuje jeho možnosti. Současně musí samotný program nutně zajistit přítomnost takových doplňků a umožnit jim plnit jejich účel.

To znamená, že například chcete-li vytvořit zásuvný modul pro grafický editor, který by prováděl konverzi obrázků, musíte v zásuvném modulu poskytnout alespoň dvě funkce (a podle toho tyto funkce volat v programu) - funkci to by vrátilo název zásuvného modulu (a/nebo jeho typ) pro přidání tohoto zásuvného modulu do nabídky (nebo panelu nástrojů), plus hlavní funkcí je odeslat a přijmout obrázek. Tito. nejprve program vyhledá pluginy, poté pro každý nalezený zavolá svou identifikační funkci s přesně definovaným názvem (například GetPluginName) a přidá požadovanou položku do nabídky, poté, pokud uživatel tuto položku vybral, zavolá druhá funkce, která předá vstupní obrázek (nebo název souboru, obsahující tento obrázek), a tato funkce zase obrázek zpracuje a vrátí v nové podobě (nebo název souboru s novým obrázkem). V tom je celý smysl pluginu... :-)

Epilog

Tento článek ukazuje hlavní aspekty používání a vytváření knihoven DLL v Borland Delphi. Pokud máte nějaké dotazy - pošlete mi je na e-mail: [e-mail chráněný], a ještě lépe - napište do konference tohoto webu, aby ostatní uživatelé viděli vaši otázku a pokusili se na ni odpovědět!

Karikh Nikolaj. Moskevská oblast, Žukovskij


Vzhledem k rychlému rozvoji programovacích technologií se stále více lidí potýká s problémem zvýšení schopností svých programů. Tento článek je věnován této problematice, konkrétně programování DLL v Borland Delphi. Navíc, protože se dotkneme problematiky používání DLL, dotkneme se i importu funkcí z cizích DLL (včetně systémových, tzn. WinAPI).

DLL aplikace

Proč jsou tedy knihovny DLL potřebné a kde se používají?. Zde je jen několik oblastí jejich použití:

Samostatné knihovny obsahující další funkce užitečné pro programátory.

Například funkce pro práci s řetězci, nebo složité knihovny pro převod obrázků.

Obchody se zdroji. V DLL můžete ukládat nejen programy a funkce, ale také všechny druhy zdrojů - ikony, obrázky, pole řetězců, nabídky atd.

Podpora knihoven. Příkladem jsou knihovny takových známých balíčků jako: DirectX, ICQAPI (API pro ICQ), OpenGL atd.

Části programu. DLL může například ukládat okna programu (formuláře) atd.

Pluginy. - Tam je skutečný prostor pro myšlenky programátora! Pluginy jsou doplňky k programu, které rozšiřují jeho možnosti. V tomto článku se například podíváme na teorii tvorby pluginu pro vlastní program.

Sdílený zdroj. DLL (Dynamic Link Library) může být využíváno více programy nebo procesy najednou (tzv. sdílení je sdílený zdroj)

Stručný popis funkcí a triků pro práci s DLL

Jaké triky a funkce tedy musíte použít pro práci s DLL? Pojďme analyzovat dvě metody pro import funkcí z knihovny:

1 způsob. Vazba DLL k programu. Toto je nejjednodušší a nejjednodušší způsob použití funkcí importovaných z knihovny DLL. Tato metoda má však (a měli byste tomu věnovat pozornost) velmi významnou nevýhodu - pokud není nalezena knihovna, kterou program používá, program se jednoduše nespustí, zobrazí chybu a hlásí, že zdroj DLL nebyl nalezen. A knihovna bude prohledána: v aktuálním adresáři, v adresáři programu, v adresáři WINDOWSSYSTEM atd.

Takže pro začátek - obecná forma této techniky:

Implementace
...
functionName FunctionName(Par1: Par1Type; Par2: Par2Type; ...): ReturnType; stdcall; externí název "DLLNAME.DLL" název "FunctionName" index FuncIndex;
// nebo (pokud to není funkce, ale procedura):
procedure NázevProcedury(Par1: Par1Typ; Par2: Par2Typ; ...); stdcall; externí název "DLLNAME.DLL" název "ProcedureName" index ProcIndex;

Zde: FunctionName (nebo ProcedureName) - název funkce (nebo procedury), která bude použita ve vašem programu;

Par1, Par2, ... - názvy parametrů funkce nebo procedury;
Par1Type, Par2Type,...- typy parametrů funkce nebo procedury (například Integer);
návratový typ- typ návratové hodnoty (pouze pro funkci);
stdcall- direktiva, která se musí přesně shodovat s direktivou použitou v samotné DLL;
externí "DLLNAME.DLL"- direktiva určující název externí DLL, ze které bude daná funkce nebo procedura importována (v tomto případě DLLNAME.DLL);
název "FunctionName"("ProcedureName") je direktiva, která určuje přesný název funkce v samotné knihovně DLL.

Toto je volitelná direktiva, která vám umožňuje používat v programu funkci, která má název odlišný od skutečného (který má v knihovně);
index FunctionIndex (ProcedureIndex) - direktiva, která určuje pořadové číslo funkce nebo procedury v DLL. Toto je také nepovinná směrnice.

2 způsobem. Dynamické načítání DLL. Jedná se o mnohem složitější, ale také elegantnější metodu. Postrádá nevýhodu prvního způsobu. Jediná věc, která je nepříjemná, je množství kódu potřebného k implementaci této techniky a problém je v tom, že funkce importovaná z DLL je dostupná pouze tehdy, když je tato DLL načtena a je v paměti ... Můžete si přečíst příklad níže, ale prozatím - krátký popis funkcí WinAPI používaných touto metodou:

LoadLibrary(LibFileName: PChar) - načte zadanou knihovnu LibFileName do paměti. V případě úspěchu funkce vrátí popisovač (THandle) do DLL v paměti.
GetProcAddress(Modul: THandle ; ProcName: PChar) - čte adresu exportované knihovní funkce. V případě úspěchu funkce vrátí popisovač (TFarProc) funkci v načtené knihovně DLL.
volná knihovna(LibModule: THandle) - Deaktivuje LibModule a uvolní paměť s ním spojenou. Je třeba poznamenat, že po zavolání této procedury již nejsou funkce této knihovny dostupné.

Praxe a příklady

Nyní je čas uvést několik příkladů použití výše uvedených metod a technik: Příklad 1. Navázání knihovny DLL na program

Implementace

(Definování funkce externí knihovny)

Funkce GetSimpleText(LangRus: Boolean): PChar; stdcall; externí "MYDLL.DLL";


začít
(a používat to)
ShowMessage(StrPas(GetSimpleText(False)));
(ShowMessage – zobrazí dialogové okno se zadaným titulkem; StrPas – převede řetězec PChar na řetězec)
konec;

Nyní to samé, ale druhým způsobem - s dynamickým načítáním: Příklad 2. Dynamické načítání DLL

(... Zde je záhlaví souboru a definice formuláře TForm1 a jeho instance Form1)

Var
Form1: TForm1;
GetSimpleText: function (LangRus: Boolean): PChar;
LibHandle: THandle;

ProcedureButton1Click(Sender: TObject);
začít
("Vyčistíme" adresu funkce od "nečistoty")
@GetSimpleText:= nula;
(Pokouším se načíst knihovnu)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Pokud je vše v pořádku)
pokud LibHandle >= 32, pak začněte
(...pak se snažíme získat adresu funkce v knihovně)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Pokud je zde vše v pořádku)
pokud @GetSimpleText nula, pak
(... pak tuto funkci zavoláme a ukážeme výsledek)
ShowMessage(StrPas(GetSimpleText(True)));
konec;
(A nezapomeňte uvolnit paměť a uvolnit DLL)
FreeLibrary(LibHandle);
konec;

POZNÁMKA: Měli byste se zdržet používání typu řetězec ve funkcích knihovny, protože při jeho používání dochází k problémům se „sdílením paměti“. Více si o tom můžete přečíst (i když v angličtině) v textu prázdného projektu DLL, který Delphi vytváří (Soubor -> Nový -> DLL). Takže raději použijte PChar a pak jej převeďte na řetězec, pokud je to potřeba, s funkcí StrPas.

Nyní pojďme analyzovat samotnou DLL přímo: Příklad 3. Zdroj projektu MYDLL.DPR
librarymydll;

Používá SysUtils, Classes;

(Funkci definujeme jako stdcall)
function GetSimpleText(LangRus: Boolean): PChar; stdcall;
začít
(V závislosti na LangRus vrátíme ruskou (pravda) nebo anglickou (nepravdivou) frázi)
pokud LangRus pak
Výsledek:= PChar("Ahoj světe!")
jiný
Výsledek:= PChar("Ahoj světe!");
konec;

(Direktiva exports určuje, které funkce budou exportovány touto knihovnou DLL)
exportuje GetSimpleText;

Začít
konec.

Umístění v DLL zdrojích a formulářích

Do DLL můžete umístit nejen funkce, ale také kurzory, obrázky, ikony, nabídky, textové řádky. U toho se nezastavíme. Uvedu pouze, že pro načtení zdroje je třeba načíst DLL a poté, co obdržíme jeho deskriptor, načíst samotný zdroj s příslušnou funkcí (LoadIcon, LoadCursor atd.). V této části se jen krátce dotkneme umístění oken aplikací (tj. formulářů v Delphi) v knihovnách DLL.

Chcete-li to provést, musíte vytvořit novou knihovnu DLL a přidat do ní nový formulář (Soubor -> Nový -> DLL a poté Soubor -> Nový formulář). Dále, pokud je formulář dialogové okno (modální formulář (bsDialog)), přidejte do DLL následující funkci (například formulář se nazývá Form1 a jeho třída je TForm1): Příklad 4. Umístění formuláře do DLL
function ShowMyDialog(Msg: PChar): Boolean; stdcall;

...
exportuje ShowMyDialog;

Funkce ShowMyDialog(Msg: PChar): Boolean;
začít
(Vytvořte instanci formuláře Form1 formuláře TForm1)
Form1:= TForm1.Create(Application);
(V Label1 zobrazujeme Msg)
Form1.Label1.Caption:= StrPas(Msg);
(Vraťte True pouze po stisknutí OK (ModalResult = mrOk))
Vysledek:= (Form1.ShowModal = mrOk);
(Uvolnění paměti)
Form1.Free;
konec;

Pokud potřebujete umístit nemodální formulář do DLL, musíte provést dvě funkce - otevření a zavření formuláře. V tomto případě musíte vynutit DLL, aby si pamatovala popisovač tohoto formuláře.

Vytváření pluginů

Zde nebudeme podrobně zvažovat pluginy, protože. výše uvedené příklady vám pomohou snadno pochopit lví podíl programování DLL. Jen připomenu, že plug-in je doplněk k programu, který rozšiřuje jeho možnosti. Současně musí samotný program nutně zajistit přítomnost takových doplňků a umožnit jim plnit jejich účel.

To znamená, že například chcete-li vytvořit zásuvný modul pro grafický editor, který by prováděl konverzi obrázků, musíte v zásuvném modulu poskytnout alespoň dvě funkce (a podle toho tyto funkce volat v programu) - funkci to by vrátilo název zásuvného modulu (a/nebo jeho typ) pro přidání tohoto zásuvného modulu do nabídky (nebo panelu nástrojů), plus hlavní funkcí je odeslat a přijmout obrázek. Tito. nejprve program vyhledá pluginy, poté pro každý nalezený zavolá svou identifikační funkci s přesně definovaným názvem (například GetPluginName) a přidá požadovanou položku do nabídky, poté, pokud uživatel tuto položku vybral, zavolá druhá funkce, která předá vstupní obrázek (nebo název souboru, obsahující tento obrázek), a tato funkce zase obrázek zpracuje a vrátí v nové podobě (nebo název souboru s novým obrázkem). V tom je celý smysl pluginu... :-)

Použití DLL v Delphi
  • Koncept knihovny DLL
  • Vytvoření DLL v Delphi (export)
  • Použití DLL v Delphi (import)
  • DLL, které používají objekty VCL pro práci s daty
  • Výjimky v DLL
  • Koncept knihovny DLL

Vzpomeňte si na proces programování v DOSu. Převod zdrojového kódu programu do strojového kódu zahrnoval dva procesy – kompilaci a propojení. Linker, který propojoval jednotlivé moduly programu, v průběhu procesu linkování umístil do kódu programu nejen deklarace funkcí a procedur, ale i jejich kompletní kód. Připravili jste jeden program takto, další, třetí... A všude byl v programu kompletně umístěn kód stejných funkcí.

Program1 Program2: : MyFunc(:) MyFunc(:) : : kód funkce MyFunc kód funkce MyFunc kód dalších funkcí kód dalších funkcí

V multitaskingovém prostředí by takový přístup byl přinejmenším lehkomyslný, protože je zřejmé, že za kreslení prvků uživatelského rozhraní, přístup k systémovým zdrojům atd. je zodpovědné velké množství stejných funkcí. by byly ve všech aplikacích zcela duplikovány, což by vedlo k rychlému vyčerpání nejdražšího zdroje – RAM. Jako řešení tohoto problému byl i na platformách podobných UNIXu navržen koncept dynamického linkování (viz obr. 2).

Jaký je ale rozdíl mezi dynamickou knihovnou (DLL) a běžnými aplikacemi? Abychom tomu porozuměli, je nutné si ujasnit pojmy úkol (úloha), instance (kopie) aplikace (instance) a modul (modul).

Při spuštění více instancí jedné aplikace Windows načte do RAM pouze jednu kopii kódu a prostředků – aplikační modul, čímž vytvoří několik samostatných datových segmentů, zásobník a frontu zpráv (viz obr. 3), z nichž každá je úkol, v chápání Windows. Kopie aplikace je kontext, ve kterém se modul aplikace spouští.

DLL - knihovna je také modul. Nachází se v paměti v jediné kopii a obsahuje kódový segment a prostředky a také datový segment (viz obrázek 4).

DLL je knihovna, na rozdíl od aplikace nemá zásobník ani frontu zpráv. Funkce umístěné v DLL se provádějí v kontextu volající aplikace pomocí jejího zásobníku. Tyto stejné funkce však používají datový segment knihovny, nikoli kopii aplikace.

Díky této organizaci DLL je dosaženo úspory paměti díky skutečnosti, že všechny spuštěné aplikace používají jeden modul DLL, přičemž ve svých modulech nejsou zahrnuty některé standardní funkce.

Často se ve formě DLL vytvářejí samostatné sady funkcí, kombinované podle toho či onoho logického znaku, podobně jako se moduly koncepčně plánují (ve smyslu jednotky) v Pascalu. Rozdíl je v tom, že funkce z modulů Pascal jsou propojeny staticky – ve fázi propojení a funkce z knihoven DLL jsou propojeny dynamicky, tedy za běhu.

Vytvoření DLL v Delphi (export)

Pro programování DLL poskytuje Delphi řadu klíčových slov a pravidel syntaxe. Hlavní věc - DLL v Delphi je stejný projekt jako program.

Zvažte šablonu DLL:


Název souboru projektu pro takovou šablonu musí být MYDLL.DPR.

Bohužel se v Delphi IDE automaticky generuje pouze projekt programu, takže musíte projekt DLL připravit ručně. Delphi 2.0 tuto nepříjemnost odstranilo.

Stejně jako program má knihovna DLL sekci použití. Inicializační část je volitelná. V části exporty jsou uvedeny funkce, které by měly být přístupné z externích aplikací.

Export funkcí (a procedur) lze provést několika způsoby:

  • podle čísla (indexu)
  • podle jména

V závislosti na tom se používá odlišná syntaxe:


Protože Windows má koncept "rezidentních funkcí" DLL, to znamená těch funkcí, které jsou v paměti po celou dobu životnosti DLL v paměti, Delphi má nástroje pro organizaci a takový druh exportu:


indexaci exportovaných funkcí pak provede automaticky Delphi a takový export bude považován za export podle názvu, který odpovídá názvu funkce. Potom se deklarace importované funkce v aplikaci musí shodovat s názvem deklarace funkce v DLL. Co se týče direktiv, které jsou již uloženy na importované funkce, o tom budeme hovořit níže.

Použití DLL v Delphi (import)

K organizaci dovozů, tzn. Delphi poskytuje standardní funkce pro přístup k funkcím exportovaným z DLL, stejně jako jejich export.

Pro výše uvedené příklady by váš program měl deklarovat funkce importované z DLL takto:


Tato metoda se nazývá statický import.

Jak jste si mohli všimnout, přípona souboru obsahujícího DLL není uvedena - standardně se předpokládají soubory *.DLL a *.EXE. Jak to tedy má být, pokud má soubor jinou příponu (například COMPLIB.DCL v Delphi), nebo pokud je vyžadována dynamická definice DLL a importovaných funkcí (např. váš program pracuje s různými grafickými formáty a pro každý z existuje samostatná knihovna DLL.)?

K vyřešení tohoto druhu problému můžete přímo přistupovat k Windows API pomocí takzvaného dynamického importu:


používá WinTypes, WinProcs, ... ; typ TMyProc = postup ; var Rukojeť: THhandle; MyImportProc: TMyProc; začít Handle:= LoadLibrary("MYDLL"); -li Rukojeť >= 32 pak (li začít@MyImportProc:= GetProcAddress(Handle, "MYEXPORTPROC"); -li MyImportProc nula pak ... (pomocí importovaného postupu) konec; FreeLibrary(Handle); konec;

Syntaktické diagramy deklarací exportu/importu, substituce výstupního bodu DLL a další příklady lze nalézt v Online Help Delphi, Object Pascal Language Guide, který je součástí Borland RAD Pack pro Delphi, a například v knize „Teach Yourself Delphi za 21 dní“.

Kromě kódu generovaného kompilátorem (nyní je více optimalizovaný) pak všechna pravidla syntaxe zůstávají stejná jako v Borland Pascal 7.0

DLL, které používají objekty VCL pro práci s daty

Při vytváření vlastní dynamické knihovny můžete použít volání funkcí z jiných knihoven DLL. Příklad takové DLL je v distribuci Delphi (X:\DELPHI\DEMOS\BD\BDEDLL). Tato DLL obsahuje formulář, který zobrazuje data z tabulky a pro přístup k ní používá objekty VCL (TTable, TDBGrid, TSession), které zase volají funkce BDE. Jak vyplývá z komentářů k tomuto příkladu, existuje omezení pro takovou knihovnu DLL: nemůže ji používat několik úloh současně. Je to proto, že objekt Session, který se automaticky vytvoří při připojení DB modulu, je inicializován pro modul a ne pro úlohu. Pokud se pokusíte načíst tuto knihovnu DLL podruhé z jiné aplikace, zobrazí se chyba. Chcete-li zabránit načítání knihovny DLL několika úlohám současně, je třeba provést některé kroky. V tomto příkladu se jedná o postup pro kontrolu, zda je knihovna DLL aktuálně používána jinou úlohou.

Výjimky v DLL

Vyhození výjimky v DLL vytvořené Delphi způsobí ukončení celé aplikace, pokud výjimka nebyla zpracována v rámci DLL. Proto je žádoucí zajistit všechny možné potíže v době vývoje DLL. Můžeme doporučit vrátit výsledek importované funkce jako řetězec nebo číslo a v případě potřeby výjimku v programu znovu vyvolat.


funkce MyFunc: tětiva; začít Snaž se (skutečný funkční kód) až na na EVýsledek: Výjimka dělat Result:=Format(DllErrorViewingTable, ) jiný Result:= Format(DllErrorViewingTable, ["Neznámá chyba"]); konec; konec;

Kód v programu:


StrResult:= MyFunc; -li StrResult "" pak vyzdvihnout Exception.Create(StrResult);

Článek Použití DLL v Delphi Sekce DLL Filesystem and PlugIns může být užitečná pro vývojáře Delphi a FreePascal.

Upozorňuji na další číslo zpravodaje, ve kterém pokračuji v diskuzi
problematika vývoje a použití DLL v Borland Delphi. Pro nové odběratele vám oznamuji,
že si mohou první část článku prohlédnout v archivu mailing listu číslo 13.
Omlouvám se těm, kteří mi napsali, ale nedostali odpověď. Pokusím se to v blízké budoucnosti napravit.
Pokračujme tedy.

Než začnete používat jakoukoli proceduru nebo funkci umístěnou v dynamické knihovně,
musíte načíst DLL do RAM. Lze provést načtení knihovny
jeden ze dvou způsobů: statické zatížení a dynamické zatížení.
Obě metody mají výhody i nevýhody.
Statické načítání znamená, že dynamická knihovna se načítá automaticky
při spuštění aplikace, která jej používá. Chcete-li použít tuto metodu stahování,
při popisu exportovaného z musíte použít externí klíčové slovo
funkce nebo procedura dynamické knihovny. DLL se automaticky načte při spuštění programu,
a stejným způsobem budete moci používat jakékoli z něj exportované podprogramy
jako by byly popsány uvnitř aplikačních modulů.
Toto je nejjednodušší způsob použití kódu umístěného v knihovně DLL.
Nevýhodou metody je, že pokud je soubor knihovny, do kterého
v aplikaci je odkaz, chybí, program se odmítne načíst.
Smyslem dynamické metody je, že knihovnu nenačtete při startu aplikace,
a ve chvíli, kdy to opravdu potřebujete. Posuďte sami, protože pokud je funkce popsaná
v dynamické knihovně se používá pouze při 10 % spuštění programu, pak absolutně ne
nemá smysl používat metodu statického zatížení. V tomto případě uvolnění knihovny z paměti
je také pod vaší kontrolou. Další výhodou této metody
Načítání DLL je zkrácení (z pochopitelných důvodů) času spuštění vaší aplikace.
A jaké jsou nevýhody této metody? Hlavní, zdá se mi, je využití
tato metoda je obtížnější než statické zatížení diskutované výše.
Nejprve musíte použít funkci LoadLibrary Windows API.
Chcete-li získat ukazatel na exportovanou proceduru nebo funkci,
použijte funkci GetProcAddress. Po ukončení používání DLL
je nutné stáhnout pomocí FreeLibrary.
Volání procedur a funkcí načtených z DLL.
Způsob volání procedur a funkcí závisí na tom, jak jste načetli dynamickou knihovnu,
ve kterých se tyto podprogramy nacházejí.
Volání funkcí a procedur ze staticky načtených knihoven DLL je poměrně jednoduché. Zpočátku v aplikaci
musí obsahovat popis exportované funkce (procedury). Poté je můžete používat
stejným způsobem, jako by byly popsány v některém z modulů vaší aplikace.
Chcete-li importovat funkci nebo proceduru obsaženou v knihovně DLL, musíte použít
externí modifikátor v jejich deklaraci. Například pro proceduru HelloWorld, kterou jsme probrali výše
do volající aplikace by měl být umístěn následující řádek:
procedure SayHello(AForm: TForm); externí myfirstdll.dll";
Externí klíčové slovo říká kompilátoru, že proceduru lze nalézt v
dynamickou knihovnu (v našem případě myfirstdll.dll).
Volání této procedury pak vypadá takto:
...
HelloWorld(já);
...
Při importu funkcí a procedur buďte obzvláště opatrní při psaní jejich názvů a rozhraní!
Faktem je, že v procesu kompilace aplikace se neprovádí žádná kontrola správnosti názvů objektů,
exportovaný z DLL nebude implementován, a pokud jste nesprávně popsali některou funkci,
pak bude výjimka vyvolána pouze za běhu aplikace.
Import z DLL lze provést podle názvu procedury (funkce), pořadového čísla, popř
s jiným jménem.
V prvním případě jednoduše deklarujete název procedury a knihovnu, ze které ji importujete.
(probírali jsme to trochu výše). Import podle sériového čísla vyžaduje, abyste uvedli právě toto číslo:
procedure HelloWorld(AForm: TForm); externí index myfirstdll.dll 15;
V tomto případě název, který zadáte postupu při importu, nemusí být stejný jako
který byl pro něj specifikován v samotné DLL. Tito. výše uvedený záznam znamená
že importujete z dynamické knihovny myfirstdll.dll proceduru, která v ní byla exportována
patnáctý a ve vaší aplikaci se tento postup nazývá SayHello.
Pokud z nějakého důvodu nepoužijete výše popsanou metodu importu,
ale přesto chcete změnit název importované funkce (postupu), můžete použít třetí metodu:
postup CoolProcedure; externí název myfirstdll.dll "DoSomethingReallyCool";
Zde se importovaná procedura CoolProcedure jmenuje DoSomethingReallyCool.
Volání procedur a funkcí importovaných z dynamicky načítaných knihoven
poněkud složitější než výše popsaná metoda. V tomto případě musíte deklarovat
ukazatel na funkci nebo postup, který se chystáte použít.
Pamatujete na rutinu HelloWorld? Podívejme se, co je pro to potřeba udělat
zavolat ji k provedení v případě dynamického načítání DLL. Nejprve ty
je nutné deklarovat typ, který by popisoval tento postup:
typ
THelloWorld = postup(AForm: TForm);
Nyní musíte načíst dynamickou knihovnu a získat ji pomocí GetProcAddress
ukazatel na proceduru, zavolejte tuto proceduru pro provedení a nakonec uvolněte DLL z paměti.
Níže je uveden kód demonstrující, jak to lze provést:

DLLInstance: THandle ;

HelloWorld:THelloWorld;

začít

(načíst DLL)

(získat ukazatel)

(zavolejte proceduru, která se má provést)

HelloWorld(Self) ;

(uvolnit DLL z RAM)

FreeLibrary(DLLInstance) ;

konec ;

Jak bylo uvedeno výše, jednou z nevýhod statického načítání DLL je nemožnost
pokračovat v aplikaci v nepřítomnosti jedné nebo více knihoven. V případě dynamického
načítání, máte možnost takové situace programově zvládnout a programu zabránit
vypadl sám od sebe. Podle hodnot vrácených funkcemi LoadLibrary a GetProcAddress můžete
zjistit, zda bylo načtení knihovny úspěšné a zda v ní byl nalezen postup požadovaný aplikací.
Níže uvedený kód to ukazuje.

procedure TForm1.DynamicLoadBtnClick (Sender: TObject ) ;

typ

THelloWorld = postup (AForm: TForm) ;

DLLInstance: THandle ;

HelloWorld:THelloWorld;

začít

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

pokud DLLInstance = 0, pak spusťte

MessageDlg( "Nelze načíst DLL", mtError, [ mbOK] , 0 );

výstup;

konec ;

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

pokud @HelloWorld nula, pak

HelloWorld (Self)

jiný

MessageDlg( "Požadovaný postup nebyl nalezen!", mtError, [ mbOK] , 0 );

FreeLibrary(DLLInstance) ;

konec ;

V DLL můžete ukládat nejen kód, ale také formuláře.
Vytváření a umísťování formulářů v dynamické knihovně se navíc příliš neliší od práce
s formuláři v běžném projektu. Nejprve se podíváme na to, jak můžete napsat knihovnu,
obsahující formuláře, a pak budeme hovořit o použití technologie MDI v DLL.
Vývoj DLL obsahující formulář předvedu na příkladu.
Nejprve tedy vytvořte nový projekt dynamické knihovny.
Chcete-li to provést, vyberte položku nabídky Soubor|Nový a poté poklepejte na ikonu DLL.
Poté uvidíte něco jako následující kód:

Uložte výsledný projekt. Říkejme tomu DllForms.dpr.
Nyní musíme vytvořit nový formulář. To lze provést různými způsoby.
Například výběrem položky nabídky Soubor|Nový formulář. Přidejte do formuláře některé součásti.
Formulář pojmenujeme DllForm a výslednou jednotku uložíme jako DllFormUnit.pas .
Vraťme se do hlavního modulu projektu a umístíme do něj funkci ShowForm, jejíž úkol bude obsahovat
vytvoření formuláře a jeho zobrazení na obrazovce. Použijte k tomu níže uvedený kód.

Formulář: TDLLForm;

začít

Vysledek:= Form.ShowModal ;

Form.Free ;

konec ;

Upozorňuji na to, že aby byl projekt sestaven bez chyb, je nutné přidat do sekce použití modul Formuláře.
Naši funkci exportujeme pomocí klíčového slova exports:
exportů
ShowForm;
Zkompilujeme projekt a získáme soubor dllforms.dll. Tyto jednoduché kroky jsou vším
co musíte udělat pro sběr Všimněte si, že funkce ShowForm je deklarována pomocí klíčového slova stdcall.
Signalizuje kompilátoru, aby při exportu funkce použil konvenci
podle standardní konvence volání. Export funkce tímto způsobem vytvoří
možnost využít vyvinutou DLL nejen v aplikacích vytvořených v Delphi.
Volací konvence definují způsob předávání argumentů při volání funkce.
Existuje pět základních konvencí: stdcall, cdecl, pascal, register a safecall.
Více se o tom můžete dozvědět v sekci "Calling Conventions" souboru nápovědy Delphi.
Všimněte si také, že hodnota vrácená funkcí ShowForm,
odpovídá hodnotě ShowModal. Tímto způsobem můžete předat nějaké informace
o stavu formuláře do volající aplikace.
Níže jsou uvedeny dva výpisy, z nichž první obsahuje úplný kód souboru
DLL projekt (modul s formulářem zde není zobrazen) a druhým je modul volající aplikace,
který využívá knihovnu, kterou jsme právě vyvinuli.

knihovna DllForms;

používá

DllFormUnit v "DllFormUnit.pas" (DllForm) ;

($R*.RES)

function ShowForm: Integer ; stdcall;

Formulář: TDLLForm;

začít

Form:= TDLLForm.Create(Application) ;

Vysledek:= Form.ShowModal ;

Form.Free ;

konec ;

začít

konec.


jednotka TestAppUnit;

rozhraní

používá

Windows, Zprávy, SysUtils, Třídy, Grafika,

Ovládací prvky, formuláře, dialogy, StdCtrls;

typ

TForm1 = class(TForm)

Tlačítko1: TButton;

procedure Button1Click(Sender: TObject ) ;

soukromé

(soukromá prohlášení)

veřejnost

(Veřejná prohlášení)

konec ;

Form1: TForm1;

function ShowForm: Integer ; stdcall;

Externí "dllforms.dll" ;

implementace

($R *.DFM)

procedure TForm1.Button1Click (Sender: TObject ) ;

začít

konec ;

konec.

Při exportu funkce bylo také použito klíčové slovo stdcall.
Zvláštní pozornost byste měli věnovat práci s podřízenými formuláři v knihovně DLL. Pokud např.
ve volající aplikaci má hlavní formulář vlastnost FormStyle nastavenou na MDIForm,
poté, když se pokusíte volat z DLL formuláře MDIChild, na obrazovce se objeví chybová zpráva,
který bude říkat, že neexistuje žádný aktivní formulář MDI.
V okamžiku, kdy se pokusíte zobrazit své podřízené okno, VCL zkontroluje správnost
vlastnost FormStyle hlavního formuláře aplikace. V našem případě se však zdá být vše v pořádku.
Tak jaká je dohoda? Problém je v tom, že při provádění takové kontroly se bere v úvahu objekt Application,
vlastněné nikoli volající aplikací, ale samotnou dynamickou knihovnou.
No, samozřejmě, protože v DLL není žádný hlavní formulář, kontrola dává chybu.
Aby k této situaci nedošlo, je nutné přiřadit k objektu Application dynamickou knihovnu
objekt Application volající aplikace. Přirozeně to bude fungovat pouze tehdy, pokud
když je volajícím programem aplikace VCL. Navíc před vyjmutím knihovny z paměti
je nutné vrátit hodnotu objektu Application knihovny do původního stavu.
To umožní správci paměti vyčistit RAM obsazenou knihovnou.
Proto je třeba uložit ukazatel na nativní aplikační objekt knihovny.
v globální proměnné, kterou lze použít při obnově její hodnoty.
Vraťme se tedy trochu zpět a vyjmenujme si kroky, které potřebujeme k práci s umístěným
ve formulářích DLL MDIChild.
V dynamické knihovně vytvoříme globální proměnnou typu TApplication.
Ukazatel na objekt Application DLL ukládáme do globální proměnné.
Objektu Application dynamické knihovny přiřadíme ukazatel na Application
aplikace pro volání.
Vytvoříme formulář MDIChild a pracujeme s ním.
Hodnotu objektu Application dynamické knihovny vrátíme do původního stavu
a uvolněte DLL z paměti.
První krok je jednoduchý. Stačí umístit následující kód do horní části modulu DLL:
var
DllApp: TApplication;
Poté vytvoříme proceduru, která změní hodnotu objektu Application a vytvoří podřízený formulář.
Postup může vypadat nějak takto:

procedure ShowMDICild(MainApp: TApplication) ;

Dítě: TMDIChild;

začít

pokud ne Assigned(DllApp), pak začněte

DllApp:= Aplikace;

Aplikace:= MainApp;

konec ;

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

Child.Show ;

konec ;

Vše, co nyní musíme udělat, je zajistit vrácení hodnoty objektu Application.
do původního stavu. Děláme to pomocí postupu MyDllProc:

procedure MyDLLProc(Důvod: Integer ) ;

začít

if Reason = DLL_PROCESS_DETACH pak

(Knihovna DLL se uvolňuje. Obnovuje se hodnota ukazatele aplikace)

if Assigned(DllApp) then

Aplikace:= DllApp;

konec ;

místo závěru.
Použití dynamických knihoven není tak složité, jak by se na první pohled mohlo zdát.

Použití dynamických knihoven není tak složité, jak by se na první pohled mohlo zdát.
DLL poskytují nejširší možnosti pro optimalizaci výkonu aplikací,
i práce samotných programátorů. Použijte DLL a možná bude váš život jednodušší!
http://subscribe.ru/
E-mailem: [e-mail chráněný] Vyhledávání
na APORT na Subscribe.Ru

Nejednou bylo nutné dostávat dopisy s žádostí o vytvoření a použití DLL v Delphi. V tomto článku se budeme zabývat vším a vytvoříme si vlastní knihovnu. Dynamicky propojené knihovny (Dinamic Link Library) umožňují různým aplikacím používat při své práci společnou sadu zdrojů. Důležité je, že procedury a funkce umístěné v DLL jsou prováděny uvnitř procesu, který je používá. DLL poskytuje všem aplikacím jedinou kopii prostředku, kterou sdílejí všechny aplikace, které ji požadují, na rozdíl od rutin, které spouštějí samostatnou kopii pro každou aplikaci, která je volá. V rozdílu mezi DLL a podprogramy lze také zahrnout skutečnost, že DLL mohou exportovat pouze procedury a funkce, ale ne typy, konstanty atd.

Rád bych uvedl výňatek z "Lekce o Delphi" o struktuře DLL v paměti:

DLL je knihovna, na rozdíl od aplikace nemá zásobník ani frontu zpráv. Funkce umístěné v DLL se provádějí v kontextu volající aplikace pomocí jejího zásobníku. Tyto stejné funkce však používají datový segment knihovny, nikoli kopii aplikace. Díky této organizaci DLL je dosaženo úspory paměti díky skutečnosti, že všechny spuštěné aplikace používají jeden modul DLL, přičemž ve svých modulech nejsou zahrnuty některé standardní funkce. Často se ve formě DLL vytvářejí samostatné sady funkcí, kombinované podle toho či onoho logického znaku, podobně jako se moduly koncepčně plánují (ve smyslu jednotky) v Pascalu. Rozdíl je v tom, že funkce z modulů Pascal jsou propojeny staticky – ve fázi propojení a funkce z knihoven DLL jsou propojeny dynamicky, tedy za běhu.

Vytvoření knihovny DLL

Struktura knihovny DLL se příliš neliší od obvyklé struktury modulu v Object Pascalu. DLL musí začínat slovem Library, za nímž následuje název knihovny. Funkce a procedury, které DLL poskytne (exportuje) ostatním uživatelům, jsou uvedeny za direktivou exports.

Pro každou proceduru nebo funkci můžete zadat její číslo pomocí direktivy Index. Pokud číslo chybí, překladač provede automatickou indexaci. Místo čísla procedury můžete použít jedinečný název, který je zadán pomocí direktivy name. Pokud není zadán název funkce ani její číslo, bude to Delphi vnímat jako export podle názvu, který bude odpovídat názvu funkce.

Knihovna může také obsahovat inicializační kód, který bude spuštěn při jejím načtení. Je umístěn mezi začátkem a koncem. Zde je obecná struktura DLL:

LibraryFirst_Dll; používá<используемые модули>; <объявления и описания функций>exportů<экспортируемые функции> <описание процедур и функций>začít<инициализационная часть>konec.

Příklady popisu exportovaných funkcí uvedu v sekci exporty:

Exportuje funkci1 index 2; Název funkce2 "My_sqr"; funkce3;

Jak už asi tušíte, název funkce se nemusí shodovat s názvem exportu!!!

Využití DLL

Modul, který potřebuje používat procedury a funkce z knihovny DLL, musí používat externí direktivu. Existují dva způsoby použití DLL (dynamické a statické). V prvním případě aplikace, která volá funkci z DLL, zná název knihovny a její vstupní bod a předpokládá se, že se knihovna nemění. V druhém případě byste se před použitím DLL měli ujistit, že požadovaná knihovna existuje a že obsahuje potřebný podprogram.

Podprogram můžete importovat podle jeho názvu a čísla. Vyhledávání podprogramu podle čísla je rychlejší, ale vždy pohodlné.

Níže jsou uvedeny příklady importu funkcí z naší First_DLL, která byla popsána v příkladu:

( import podle zadaného jména ) Funkce ImportByName; externí název "First_DLL" "My_sqr"; ( import podle indexu ) Funkce ImportByOrdinal; externí "First_DLL" index 2; (import podle původního názvu) Funkce Fucntion3; externí "First_DLL";

Podívali jsme se na statickou metodu použití DLL. V některých případech však není předem známo, která knihovna bude vyžadována, takže byste měli použít dynamickou metodu:

Používá WinTypes, WinProcs, ... ; typ TMyProc = postup ; var Rukojeť: THandle; MyImportProc: TMyProc; begin Handle:=LoadLibrary("FirstDLL"); if Handle>=32 then ( if<=32 - ошибка! } begin @MyImportProc:=GetProcAddress(Handle,"My_sqr"); if MyImportProc<>nil then ...... (zde použijeme výslednou funkci) end; FreeLibrary(Handle); konec;

Ale podle mého názoru není vše, co je zde napsáno, příliš jasné a chci skutečné dokončené příklady. Vždy mě zamrzelo, když bylo v článcích málo příkladů, ale pouze jedna teorie, proto vám dávám do pozornosti jednoduchý příklad použití DLL.

Klikněte na nabídku Soubor -> Nový a vyberte DLL. Uložte hotovou šablonu podle návrhu pod názvem Project1.dpr.

Níže je jeho úplný kód:

projekt knihovny1; používá SysUtils,Classes; Funkce Funkce1(x,y:celé číslo):celé číslo; vývozní; bginresult:=x+y; konec; Funkce Funkce2(x,y:skutecne):skutecne; vývozní; vart:skutečný; begin t:=exp(y*ln(x)); vysledek:=t; konec; exportuje funkci1 index 1, název funkce2 "My_sqr"; začátek konec.

Jsou zde dvě funkce, první vypočítá součet dvou čísel a exportuje se podle čísla a druhá vypočítá x na mocninu y a exportuje se podle názvu.

Nyní vytvořte nový projekt a uložte jej pod jiným názvem, například DemoDLL. Umístíme na formulář dvě tlačítka, kliknutím na první vyvoláte první proceduru a kliknutím na druhé vyvoláte druhou. Zde je úplný kód pro tento demo projekt:

ukázka jednotky; rozhraní používá Windows, zprávy, SysUtils, třídy, grafiku, ovládací prvky, formuláře, dialogy, StdCtrls; typ TForm1 = class(TForm) Button1: TButton; Tlačítko2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private ( Private deklarace ) public ( Public deklarace ) end; var Form1: TForm1; implementace ($R *.DFM) function ImportSumm(x,y:integer):integer; externí "Projekt1" index 1; //import podle číselné funkce ImportSqr(x,y:real):real; externí název "Project1" "My_sqr"; //import podle názvu procedure TForm1.Button1Click(Sender: TObject); vart:skutečný; begin //Zjistěte, jak moc bude dvě až třetí mocnina t:=ImportSqr(2,3); Showmessage(FloatTostr(t)); konec; procedure TForm1.Button2Click(Sender: TObject); vart:integer; begin //Zjistěte, kolik bude 10+10 t:=ImportSumm(10,10); Showmessage(IntTostr(t)); konec; konec.

Delphi programátor, MySQL. Vysokoškolské vzdělání. Specialita: software informačních technologií.