W związku z szybkim rozwojem technologii programowania coraz więcej osób boryka się z problemem zwiększania możliwości swoich programów. Ten artykuł jest poświęcony temu zagadnieniu, a mianowicie programowaniu DLL w Borland Delphi. Ponadto, ponieważ poruszymy kwestie korzystania z bibliotek DLL, poruszymy również kwestię importowania funkcji z bibliotek DLL innych osób (w tym systemowych, tj. WinAPI).

Aplikacje DLL

Dlaczego więc biblioteki DLL są potrzebne i gdzie są używane?... Oto tylko kilka z ich obszarów zastosowań:

  • Oddzielne biblioteki, zawierający dodatkowe funkcje przydatne dla programistów. Na przykład funkcje do pracy z ciągami lub złożone biblioteki do konwersji obrazów.
  • Magazyny surowców. W bibliotece DLL możesz przechowywać nie tylko programy i funkcje, ale także wszelkiego rodzaju zasoby - ikony, obrazy, tablice ciągów, menu itp.
  • Biblioteki wsparcia. Przykładem są biblioteki tak znanych pakietów jak: DirectX, ICQAPI(API dla ICQ), OpenGL itp.
  • Części programu. Na przykład biblioteka DLL może przechowywać okna programu (formularze) itp.
  • Wtyczki(Wtyczki). - To jest prawdziwy zakres myśli programisty! Wtyczki to dodatki do programu rozszerzające jego możliwości. Na przykład w tym artykule przyjrzymy się teorii tworzenia wtyczki do własnego programu.
  • Zasób udostępniony. DLL( Biblioteka linków dynamicznych) może być używany przez kilka programów lub procesów jednocześnie (tzw. dzielenie się- udostępniony zasób)

Krótki opis funkcji i sztuczek do pracy z DLL

Jakich sztuczek i funkcji potrzebujesz do pracy z biblioteką DLL? Przeanalizujmy dwie metody importowania funkcji z biblioteki:

1 sposób. Powiązanie biblioteki DLL z programem. Jest to najprostsza i najłatwiejsza metoda korzystania z funkcji importowanych z biblioteki DLL. Jednak (i ​​należy na to zwrócić uwagę) ta metoda ma bardzo istotną wadę - jeśli biblioteka, z której korzysta program, nie zostanie znaleziona, program po prostu nie uruchomi się, podając błąd i zgłaszając, że nie znaleziono zasobu DLL. A biblioteka zostanie przeszukana: w bieżącym katalogu, w katalogu programu, w katalogu WINDOWS\SYSTEM itd.
Na początek - ogólna forma tej techniki:

realizacja
...
funkcjonować FunctionName(Par1: Par1Type; Par2: Par2Type; ...): ReturnType; standardowe wywołanie; zewnętrzny„NAZWADLL.DLL” Nazwa„Nazwa funkcji” indeks FuncIndex;
// lub (jeśli nie funkcja, ale procedura):
procedura ProceduraName(Par1: Par1Type; Par2: Par2Type; ...); standardowe wywołanie; zewnętrzny„NAZWADLL.DLL” Nazwa"nazwa procedury" indeks Indeks Proc;

Tutaj: Nazwa funkcji(lub Nazwa procedury) - nazwa funkcji (lub procedury), która będzie używana w Twoim programie;
Par1, Par2, ...- nazwy parametrów funkcji lub procedur;
Par1Type, Par2Type, ...- typy parametrów funkcji lub procedury (np. Liczba całkowita);
typ zwrotu- typ wartości zwracanej (tylko dla funkcji);
standardowe wywołanie- dyrektywa, która musi dokładnie odpowiadać tej użytej w samej bibliotece DLL;
zewnętrzne "DLLNAME.DLL"- dyrektywa określająca nazwę zewnętrznej biblioteki DLL, z której zostanie zaimportowana dana funkcja lub procedura (w tym przypadku - DLLNAME.DLL);
nazwa "FunctionName" ("ProcedureName")- dyrektywa określająca dokładną nazwę funkcji w samej bibliotece DLL. Jest to opcjonalna dyrektywa, która pozwala na użycie w programie funkcji o nazwie innej niż prawdziwa (którą ma w bibliotece);
indeks FunctionIndex(ProcedureIndex)- dyrektywa określająca numer porządkowy funkcji lub procedury w bibliotece DLL. Jest to również dyrektywa opcjonalna.

2 sposób. Dynamiczne ładowanie DLL. To znacznie bardziej złożona, ale i bardziej elegancka metoda. Jest pozbawiona wady pierwszej metody. Jedyne, co jest nieprzyjemne, to ilość kodu wymaganego do zaimplementowania tej techniki, a trudność polega na tym, że funkcja zaimportowana z biblioteki DLL jest dostępna tylko wtedy, gdy ta biblioteka DLL jest załadowana i znajduje się w pamięci... Możesz przeczytać poniższy przykład, ale na razie krótki opis funkcji WinAPI wykorzystywanych przez tę metodę:

Załaduj bibliotekę(nazwa pliku biblioteki: PChar) - ładowanie określonej biblioteki LibFileName do pamięci. Po pomyślnym zakończeniu funkcja zwraca uchwyt ( Uchwyt) DLL w pamięci.
PobierzProcAdres(Moduł: Uchwyt; Nazwa procesu: PChar) - odczytuje adres wyeksportowanej funkcji bibliotecznej. Po pomyślnym zakończeniu funkcja zwraca uchwyt ( TFarProc) funkcje w załadowanej bibliotece DLL.
darmowa biblioteka(LibModuł: Uchwyt) - unieważnia LibModule i zwalnia powiązaną z nim pamięć. Należy zauważyć, że po wywołaniu tej procedury funkcje tej biblioteki nie są już dostępne.

Praktyka i przykłady

Cóż, teraz nadszedł czas, aby podać kilka przykładów zastosowania powyższych metod i technik:

Teraz to samo, ale w drugi sposób - z dynamicznym ładowaniem:

(... Oto nagłówek pliku i definicja formularza TForm1 i jego instancja Form1)

var
Form1: TForm1;
PobierzSimpleText: funkcjonować(język rus.: boolowski): PChar;
Libuchwyt: THuchwyt;

procedura Button1Click(Sender: TObject);
zaczynać
(„Czyścimy” adres funkcji z „brudu”)
@GetSimpleText:= zero;
(Próbuję załadować bibliotekę)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Jeśli wszystko jest w porządku)
jeśli LibHandle >= 32 to zacznij
(... wtedy próbujemy uzyskać adres funkcji w bibliotece)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Jeśli wszystko tutaj jest w porządku)
jeśli @GetSimpleText<>w takim razie zero
(... potem wywołujemy tę funkcję i pokazujemy wynik)
ShowMessage(StrPas(GetSimpleText(True)));
koniec;
(I nie zapomnij zwolnić pamięci i rozładować DLL)
FreeLibrary(LibHandle);
koniec;

NOTATKA : Powinieneś powstrzymać się od używania typu string w funkcjach bibliotecznych, ponieważ występują problemy z „współdzieleniem pamięci” podczas korzystania z niego. Możesz przeczytać więcej na ten temat (choć po angielsku) w tekście pustego projektu DLL, który tworzy Delphi (Plik -> Nowy -> DLL). Więc lepiej jest użyć PChar, a następnie przekonwertować go na łańcuch za pomocą StrPas, jeśli to konieczne.

Cóż, teraz przeanalizujmy samą bibliotekę DLL bezpośrednio:

Umieszczenie w zasobach i formularzach DLL

W bibliotece DLL można umieszczać nie tylko funkcje, ale także kursory, obrazki, ikony, menu, linie tekstu. Nie poprzestaniemy na tym. Zaznaczę tylko, że aby załadować zasób, musisz załadować bibliotekę DLL, a następnie, po otrzymaniu jej deskryptora, załadować sam zasób odpowiednią funkcją (LoadIcon, LoadCursor itp.). W tej sekcji omówimy tylko pokrótce umieszczanie okien aplikacji (tj. formularzy w Delphi) w bibliotekach DLL.

W tym celu należy utworzyć nową bibliotekę DLL i dodać do niej nowy formularz (Plik -> Nowy -> DLL, a następnie Plik -> Nowy formularz). Ponadto, jeśli formularz jest oknem dialogowym (formularz modalny (bsDialog)), dodaj następującą funkcję do biblioteki DLL (na przykład formularz nazywa się Form1, a jego klasa to TForm1):

Jeśli potrzebujesz umieścić niemodalny formularz w bibliotece DLL, musisz wykonać dwie funkcje - otwieranie i zamykanie formularza. W takim przypadku musisz zmusić bibliotekę DLL do zapamiętania uchwytu do tego formularza.

Tworzenie wtyczek

Tutaj nie będziemy szczegółowo rozważać wtyczek, ponieważ. podane powyżej przykłady pomogą Ci łatwo zrozumieć lwią część programowania DLL. Przypomnę tylko, że wtyczka jest dodatkiem do programu rozszerzającym jego możliwości. Jednocześnie sam program musi koniecznie przewidywać obecność takich dodatków i pozwalać im na spełnienie ich celu.

Czyli np. aby utworzyć wtyczkę do edytora graficznego, która wykonałaby konwersję obrazu, należy we wtyczce podać co najmniej dwie funkcje (i odpowiednio wywołać te funkcje w programie) - funkcję który zwróciłby nazwę wtyczki (i/lub jej typ), aby dodać tę wtyczkę do menu (lub paska narzędzi), a główną funkcją jest wysyłanie i odbieranie obrazu. Tych. najpierw program wyszukuje wtyczki, następnie za każdą znalezioną wywołuje funkcję identyfikującą o ściśle określonej nazwie (np. GetPluginName) i dodaje żądaną pozycję do menu, a następnie, jeśli użytkownik wybrał tę pozycję, wywołuje druga funkcja, która przekazuje obraz wejściowy (lub nazwę pliku zawierającego ten obraz), a ta funkcja z kolei przetwarza obraz i zwraca go w nowej postaci (lub nazwę pliku z nowym obrazem). To jest cały sens wtyczki... :-)

Epilog

Ten artykuł przedstawia główne aspekty używania i tworzenia bibliotek DLL w Borland Delphi. Jeśli masz jakieś pytania - wyślij je do mnie e-mailem: [e-mail chroniony], a jeszcze lepiej - napisz na konferencji tej strony, aby inni użytkownicy mogli zobaczyć Twoje pytanie i spróbować na nie odpowiedzieć!

Karikh Nikołaj. Obwód moskiewski, Żukowski


W związku z szybkim rozwojem technologii programowania coraz więcej osób boryka się z problemem zwiększania możliwości swoich programów. Ten artykuł jest poświęcony temu zagadnieniu, a mianowicie programowaniu DLL w Borland Delphi. Ponadto, ponieważ poruszymy kwestię korzystania z bibliotek DLL, poruszymy również kwestię importowania funkcji z bibliotek DLL innych osób (w tym systemowych, tj. WinAPI).

Aplikacje DLL

Dlaczego więc biblioteki DLL są potrzebne i gdzie są używane?... Oto tylko kilka z ich obszarów zastosowań:

Osobne biblioteki zawierające dodatkowe funkcje przydatne dla programistów.

Na przykład funkcje do pracy z ciągami lub złożone biblioteki do konwersji obrazów.

Magazyny surowców. W bibliotece DLL możesz przechowywać nie tylko programy i funkcje, ale także wszelkiego rodzaju zasoby - ikony, obrazy, tablice ciągów, menu itp.

Biblioteki wsparcia. Przykładem są biblioteki tak znanych pakietów jak: DirectX, ICQAPI (API dla ICQ), OpenGL itp.

Części programu. Na przykład biblioteka DLL może przechowywać okna programu (formularze) itp.

Wtyczki. - To jest prawdziwy zakres myśli programisty! Wtyczki to dodatki do programu rozszerzające jego możliwości. Na przykład w tym artykule przyjrzymy się teorii tworzenia wtyczki do własnego programu.

Zasób udostępniony. DLL (Dynamic Link Library) może być używany przez kilka programów lub procesów jednocześnie (tzw. udostępnianie jest zasobem współdzielonym)

Krótki opis funkcji i sztuczek do pracy z DLL

Jakich sztuczek i funkcji potrzebujesz do pracy z biblioteką DLL? Przeanalizujmy dwie metody importowania funkcji z biblioteki:

1 sposób. Powiązanie biblioteki DLL z programem. Jest to najprostsza i najłatwiejsza metoda korzystania z funkcji importowanych z biblioteki DLL. Jednak (i ​​należy na to zwrócić uwagę) ta metoda ma bardzo istotną wadę - jeśli biblioteka, z której korzysta program, nie zostanie znaleziona, program po prostu nie uruchomi się, podając błąd i zgłaszając, że nie znaleziono zasobu DLL. A biblioteka zostanie przeszukana: w bieżącym katalogu, w katalogu programu, w katalogu WINDOWSSYSTEM itd.

Na początek - ogólna forma tej techniki:

Realizacja
...
function nazwa_funkcji(Par1:Par1Type;Par2:Par2Type; ...): ReturnType; wywołanie standardowe; nazwa zewnętrzna "DLLNAME.DLL" indeks "FunctionName" FuncIndex;
// lub (jeśli nie funkcja, ale procedura):
procedura ProcedureName(Par1: Par1Type; Par2: Par2Type; ...); wywołanie standardowe; nazwa zewnętrzna "DLLNAME.DLL" "ProcedureName" index ProcIndex;

Tutaj: FunctionName (lub ProcedureName) - nazwa funkcji (lub procedury), która będzie używana w twoim programie;

Par1, Par2, ... - nazwy parametrów funkcji lub procedury;
Par1Type, Par2Type, ...- typy parametrów funkcji lub procedury (np. Integer);
typ zwrotu- typ wartości zwracanej (tylko dla funkcji);
standardowe wywołanie- dyrektywa, która musi dokładnie odpowiadać tej użytej w samej bibliotece DLL;
zewnętrzne "DLLNAME.DLL"- dyrektywa określająca nazwę zewnętrznej biblioteki DLL, z której zostanie zaimportowana dana funkcja lub procedura (w tym przypadku DLLNAME.DLL);
nazwa "Nazwa Funkcji"(„ProcedureName”) to dyrektywa, która określa dokładną nazwę funkcji w samej bibliotece DLL.

Jest to opcjonalna dyrektywa, która pozwala na użycie w programie funkcji o nazwie innej niż prawdziwa (którą ma w bibliotece);
index FunctionIndex (ProcedureIndex) — dyrektywa określająca numer porządkowy funkcji lub procedury w bibliotece DLL. Jest to również dyrektywa opcjonalna.

2 sposób. Dynamiczne ładowanie DLL. To znacznie bardziej złożona, ale i bardziej elegancka metoda. Jest pozbawiona wady pierwszej metody. Jedyne, co jest nieprzyjemne, to ilość kodu wymaganego do zaimplementowania tej techniki, a trudność polega na tym, że funkcja zaimportowana z biblioteki DLL jest dostępna tylko wtedy, gdy ta biblioteka DLL jest załadowana i znajduje się w pamięci... Możesz przeczytać poniższy przykład, ale na razie krótki opis funkcji WinAPI wykorzystywanych przez tę metodę:

Załaduj bibliotekę(LibFileName: PChar) - załaduj określoną bibliotekę LibFileName do pamięci. Po pomyślnym zakończeniu funkcja zwraca uchwyt (THandle) do biblioteki DLL w pamięci.
PobierzProcAdres(Moduł: THandle ; ProcName: PChar) - odczytuje adres eksportowanej funkcji bibliotecznej. Po pomyślnym zakończeniu funkcja zwraca uchwyt (TFarProc) do funkcji w załadowanej bibliotece DLL.
darmowa biblioteka(LibModule: THandle) — Unieważnia LibModule i zwalnia powiązaną z nim pamięć. Należy zauważyć, że po wywołaniu tej procedury funkcje tej biblioteki nie są już dostępne.

Praktyka i przykłady

Cóż, teraz nadszedł czas, aby podać kilka przykładów użycia powyższych metod i technik: Przykład 1. Powiązanie biblioteki DLL z programem

Realizacja

(Definiowanie funkcji biblioteki zewnętrznej)

Funkcja GetSimpleText(LangRus: Boolean): PChar; wywołanie standardowe; zewnętrzne "MYDLL.DLL";


zaczynać
(i użyj go)
ShowMessage(StrPas(GetSimpleText(False)));
(ShowMessage — wyświetla okno dialogowe z określonym podpisem; StrPas — konwertuje łańcuch PChar na łańcuch)
koniec;

Teraz to samo, ale w drugi sposób - z dynamicznym ładowaniem: Przykład 2. Dynamiczne ładowanie DLL

(... Oto nagłówek pliku i definicja formularza TForm1 i jego instancja Form1)

Var
Form1: TForm1;
GetSimpleText: funkcja (LangRus: Boolean): PChar;
Libuchwyt: THuchwyt;

ProcedureButton1Click(Sender: TObject);
zaczynać
(„Czyścimy” adres funkcji z „brudu”)
@GetSimpleText:= zero;
(Próbuję załadować bibliotekę)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Jeśli wszystko jest w porządku)
jeśli LibHandle >= 32 to zacznij
(... wtedy próbujemy uzyskać adres funkcji w bibliotece)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Jeśli wszystko tutaj jest w porządku)
jeśli @GetSimpleText nil to
(... potem wywołujemy tę funkcję i pokazujemy wynik)
ShowMessage(StrPas(GetSimpleText(True)));
koniec;
(I nie zapomnij zwolnić pamięci i rozładować DLL)
FreeLibrary(LibHandle);
koniec;

NOTATKA: Powinieneś powstrzymać się od używania typu string w funkcjach bibliotecznych, ponieważ występują problemy z „współdzieleniem pamięci” podczas korzystania z niego. Możesz przeczytać więcej na ten temat (choć po angielsku) w tekście pustego projektu DLL, który tworzy Delphi (Plik -> Nowy -> DLL). Więc lepiej użyj PChar, a następnie przekonwertuj go na łańcuch, jeśli to konieczne, za pomocą funkcji StrPas.

Cóż, teraz przeanalizujmy bezpośrednio samą bibliotekę DLL: Przykład 3. Źródło projektu MYDLL.DPR
bibliotekamydll;

Używa SysUtils, klas;

(Funkcję definiujemy jako stdcall)
funkcja GetSimpleText(LangRus: Boolean): PChar; wywołanie standardowe;
zaczynać
(W zależności od LangRus zwracamy frazę rosyjską (prawda) lub angielską (fałsz)
jeśli LangRus to
Wynik:= PChar("Witaj świecie!")
w przeciwnym razie
Wynik:= PChar("Witaj świecie!");
koniec;

(Dyrektywa exports określa, które funkcje będą eksportowane przez tę bibliotekę DLL)
eksportuje GetSimpleText;

Zaczynać
koniec.

Umieszczenie w zasobach i formularzach DLL

W bibliotece DLL można umieszczać nie tylko funkcje, ale także kursory, obrazki, ikony, menu, linie tekstu. Nie poprzestaniemy na tym. Zaznaczę tylko, że aby załadować zasób, musisz załadować bibliotekę DLL, a następnie, po otrzymaniu jej deskryptora, załadować sam zasób odpowiednią funkcją (LoadIcon, LoadCursor itp.). W tej sekcji omówimy tylko pokrótce umieszczanie okien aplikacji (tj. formularzy w Delphi) w bibliotekach DLL.

W tym celu należy utworzyć nową bibliotekę DLL i dodać do niej nowy formularz (Plik -> Nowy -> DLL, a następnie Plik -> Nowy formularz). Ponadto, jeśli formularz jest oknem dialogowym (formularz modalny (bsDialog)), dodaj następującą funkcję do biblioteki DLL (na przykład formularz nazywa się Form1, a jego klasa to TForm1): Przykład 4. Umieszczenie formularza w DLL
funkcja ShowMyDialog(Wiadomość: PChar): Boolean; wywołanie standardowe;

...
eksportuje ShowMyDialog;

Funkcja ShowMyDialog(Wiadomość: PChar): Boolean;
zaczynać
(Utwórz instancję formularza Form1 TForm1)
Form1:= TForm1.Create(Aplikacja);
(W Label1 wyświetlamy Msg)
Form1.Label1.Caption:= StrPas(Wiadomość);
(Zwróć True tylko po naciśnięciu przycisku OK (ModalResult = mrOk))
Wynik:= (Form1.ShowModal = mrOk);
(zwalnianie pamięci)
Form1.Free;
koniec;

Jeśli potrzebujesz umieścić niemodalny formularz w bibliotece DLL, musisz wykonać dwie funkcje - otwieranie i zamykanie formularza. W takim przypadku musisz zmusić bibliotekę DLL do zapamiętania uchwytu do tego formularza.

Tworzenie wtyczek

Tutaj nie będziemy szczegółowo rozważać wtyczek, ponieważ. podane powyżej przykłady pomogą Ci łatwo zrozumieć lwią część programowania DLL. Przypomnę tylko, że wtyczka jest dodatkiem do programu rozszerzającym jego możliwości. Jednocześnie sam program musi koniecznie przewidywać obecność takich dodatków i pozwalać im na spełnienie ich celu.

Czyli np. aby utworzyć wtyczkę do edytora graficznego, która wykonałaby konwersję obrazu, należy we wtyczce podać co najmniej dwie funkcje (i odpowiednio wywołać te funkcje w programie) - funkcję który zwróciłby nazwę wtyczki (i/lub jej typ), aby dodać tę wtyczkę do menu (lub paska narzędzi), a główną funkcją jest wysyłanie i odbieranie obrazu. Tych. najpierw program wyszukuje wtyczki, następnie za każdą znalezioną wywołuje funkcję identyfikującą o ściśle określonej nazwie (np. GetPluginName) i dodaje żądaną pozycję do menu, a następnie, jeśli użytkownik wybrał tę pozycję, wywołuje druga funkcja, która przekazuje obraz wejściowy (lub nazwę pliku zawierającego ten obraz), a ta funkcja z kolei przetwarza obraz i zwraca go w nowej postaci (lub nazwę pliku z nowym obrazem). To jest cały sens wtyczki... :-)

Korzystanie z biblioteki DLL w Delphi
  • Koncepcja biblioteki DLL
  • Tworzenie DLL w Delphi (eksport)
  • Korzystanie z biblioteki DLL w Delphi (import)
  • Biblioteki DLL, które używają obiektów VCL do pracy z danymi
  • Wyjątki w DLL
  • Koncepcja biblioteki DLL

Przypomnij sobie proces programowania w DOS. Konwersja kodu źródłowego programu na kod maszynowy obejmowała dwa procesy - kompilację i linkowanie. Podczas procesu linkowania linker, który łączył poszczególne moduły programu, umieszczał w kodzie programu nie tylko deklaracje funkcji i procedur, ale także ich kompletny kod. W ten sposób przygotowałeś jeden program, drugi, trzeci... I wszędzie kod tych samych funkcji był w całości umieszczony w programie.

Program1 Program2: : MyFunc(:) MyFunc(:) : : kod funkcji MyFunc kod funkcji MyFunc kod innych funkcji kod innych funkcji

W środowisku wielozadaniowym takie podejście byłoby co najmniej lekkomyślne, ponieważ oczywiste jest, że ogromna liczba tych samych funkcji odpowiada za rysowanie elementów interfejsu użytkownika, dostęp do zasobów systemowych itp. byłby całkowicie zduplikowany we wszystkich aplikacjach, co doprowadziłoby do szybkiego wyczerpania najdroższego zasobu - pamięci RAM. Jako rozwiązanie tego problemu, nawet na platformach typu UNIX, zaproponowano koncepcję dynamicznego łączenia (patrz rys. 2).

Ale jaka jest różnica między biblioteką Dynamic Link Library (DLL) a zwykłymi aplikacjami? Aby to zrozumieć, konieczne jest wyjaśnienie pojęć zadania (zadania), instancji (kopii) aplikacji (instancji) i modułu (modułu).

Podczas uruchamiania wielu instancji pojedynczej aplikacji system Windows ładuje do pamięci RAM tylko jedną kopię kodu i zasobów - moduł aplikacji, tworząc kilka oddzielnych segmentów danych, stos i kolejkę komunikatów (patrz rys. 3), z których każdy zestaw jest zadanie, w rozumieniu Windows. Kopia aplikacji to kontekst, w którym wykonywany jest moduł aplikacji.

DLL - biblioteka jest również modułem. Znajduje się w pamięci w jednym egzemplarzu i zawiera segment kodu i zasoby, a także segment danych (patrz rysunek 4).

DLL jest biblioteką, w przeciwieństwie do aplikacji, nie ma ani stosu, ani kolejki komunikatów. Funkcje umieszczone w bibliotece DLL są wykonywane w kontekście aplikacji wywołującej przy użyciu jej stosu. Ale te same funkcje wykorzystują segment danych biblioteki, a nie kopię aplikacji.

Dzięki takiej organizacji biblioteki DLL oszczędności pamięci są osiągane dzięki temu, że wszystkie uruchomione aplikacje korzystają z jednego modułu DLL, nie obejmując pewnych standardowych funkcji w swoich modułach.

Często w formie DLL tworzone są oddzielne zestawy funkcji, łączone według jednej lub drugiej cechy logicznej, podobnie jak moduły są koncepcyjnie planowane (w sensie jednostki) w Pascalu. Różnica polega na tym, że funkcje z modułów Pascala są łączone statycznie - na etapie łączenia, a funkcje z bibliotek DLL są łączone dynamicznie, czyli w czasie wykonywania.

Tworzenie DLL w Delphi (eksport)

Do programowania DLL, Delphi zapewnia szereg słów kluczowych i reguł składni. Najważniejsze - DLL w Delphi to ten sam projekt, co program.

Rozważ szablon DLL:


Nazwa pliku projektu takiego szablonu musi mieć postać MYDLL.DPR.

Niestety tylko projekt programu jest automatycznie generowany w środowisku Delphi IDE, więc musisz ręcznie przygotować projekt DLL. Delphi 2.0 usunęło tę niedogodność.

Podobnie jak program, biblioteka DLL ma sekcję zastosowań. Część inicjująca jest opcjonalna. Sekcja eksportu zawiera listę funkcji, do których należy uzyskać dostęp z aplikacji zewnętrznych.

Eksportowanie funkcji (i procedur) można wykonać na kilka sposobów:

  • według numeru (indeks)
  • po imieniu

W zależności od tego używana jest inna składnia:


Ponieważ Windows ma koncepcję "funkcji rezydentnych" biblioteki DLL, to znaczy tych funkcji, które są w pamięci przez cały okres istnienia biblioteki DLL w pamięci, Delphi ma narzędzia do organizowania i takiego rodzaju eksportu:


wówczas indeksowanie wyeksportowanych funkcji zostanie wykonane automatycznie przez Delphi, a taki eksport będzie uważany za eksport o nazwie zgodnej z nazwą funkcji. Wtedy deklaracja importowanej funkcji w aplikacji musi być zgodna z nazwą deklaracji funkcji w bibliotece DLL. Jeśli chodzi o dyrektywy, które są już nałożone na importowane funkcje, omówimy to poniżej.

Korzystanie z biblioteki DLL w Delphi (import)

Aby zorganizować import, tj. Dostęp do funkcji wyeksportowanych z biblioteki DLL, a także ich eksport, Delphi zapewnia standardowe udogodnienia.

Dla przykładów pokazanych powyżej, twój program powinien deklarować funkcje importowane z biblioteki DLL w następujący sposób:


Ta metoda nazywa się importem statycznym.

Jak mogłeś zauważyć, rozszerzenie pliku zawierającego bibliotekę DLL nie jest określone - domyślnie zakładane są pliki *.DLL i *.EXE. Jak to zrobić, jeśli plik ma inne rozszerzenie (na przykład COMPLIB.DCL w Delphi) lub jeśli wymagana jest dynamiczna definicja DLL i importowanych funkcji (na przykład twój program działa z różnymi formatami graficznymi i dla każdego z im jest osobna biblioteka DLL.)?

Aby rozwiązać tego rodzaju problem, możesz uzyskać bezpośredni dostęp do Windows API za pomocą tzw. dynamicznego importu:


używa Typy Win, WinProcs, ... ; rodzaj TMyProc = procedura ; var Uchwyt: T Uchwyt; MojaProc.Importowa: TMyProc; zaczynać Uchwyt:= LoadLibrary("MYDLL"); jeśli Uchwyt >= 32 następnie (jeśli zaczynać@MyImportProc:= GetProcAddress(Uchwyt, "MYEXPORTPROC"); jeśli Mój proces importu zero następnie ... (przy użyciu importowanej procedury) koniec; FreeLibrary (uchwyt); koniec;

Diagramy składni deklaracji eksportu/importu, podstawiania punktu wyjścia DLL i inne przykłady można znaleźć w pomocy online Delphi, przewodniku Object Pascal Language Guide zawartym w pakiecie Borland RAD Pack dla Delphi i na przykład w książce „Teach Yourself Delphi za 21 dni".

Poza kodem wygenerowanym przez kompilator (teraz jest bardziej zoptymalizowany), wszystkie zasady składni pozostają takie same jak w Borland Pascal 7.0

Biblioteki DLL, które używają obiektów VCL do pracy z danymi

Podczas tworzenia własnej biblioteki dynamicznej możesz używać wywołań funkcji z innych bibliotek DLL. Przykład takiej biblioteki DLL znajduje się w dystrybucji Delphi (X:\DELPHI\DEMOS\BD\BDEDLL). Ta biblioteka DLL zawiera formularz, który wyświetla dane z tabeli i używa obiektów VCL (TTable, TDBGrid, TSession), aby uzyskać do nich dostęp, które z kolei wywołują funkcje BDE. Jak wynika z komentarzy do tego przykładu, istnieje ograniczenie takiej biblioteki DLL: nie może być używana przez kilka zadań jednocześnie. Dzieje się tak, ponieważ obiekt Session, który jest tworzony automatycznie po podłączeniu modułu DB, jest inicjowany dla modułu, a nie dla zadania. Jeśli spróbujesz załadować tę bibliotekę DLL po raz drugi z innej aplikacji, pojawi się błąd. Aby zapobiec jednoczesnemu ładowaniu biblioteki DLL wielu zadań, należy wykonać kilka kroków. W tym przykładzie jest to procedura sprawdzania, czy biblioteka DLL jest aktualnie używana przez inne zadanie.

Wyjątki w DLL

Zgłoszenie wyjątku w bibliotece DLL utworzonej przez Delphi spowoduje zakończenie działania całej aplikacji, jeśli wyjątek nie został obsłużony w bibliotece DLL. Dlatego pożądane jest, aby w czasie opracowywania biblioteki DLL uwzględnić wszystkie możliwe problemy. Możemy zalecić zwrócenie wyniku importowanej funkcji w postaci ciągu lub liczby i, jeśli to konieczne, ponowne wrzucenie wyjątku w programie.


funkcjonować MojaFunkcja: strunowy; zaczynać próbować (rzeczywisty kod funkcji) oprócz na EWynik: wyjątek robić Wynik:=Format(DllErrorViewing Table, ) w przeciwnym razie Wynik:= Format(DllErrorViewingTable, ["Nieznany błąd"]); koniec; koniec;

Kod w programie:


WynikStr:= MojaFunkcja; jeśli StrWynik "" następnie podnieść Wyjątek.Utwórz(StrResult);

Artykuł Korzystanie z biblioteki DLL w Delphi Sekcja DLL Filesystem and PlugIns może być przydatna dla programistów Delphi i FreePascal.

Zwracam uwagę na kolejny numer biuletynu, w którym kontynuuję dyskusję
zagadnienia rozwoju i użytkowania DLL w Borland Delphi. Dla nowych subskrybentów informuję,
że mogą obejrzeć pierwszą część artykułu w archiwum listy mailingowej, numer 13.
Przepraszam tych, którzy do mnie napisali, ale nie otrzymali odpowiedzi. Spróbuję to naprawić w najbliższej przyszłości.
Więc kontynuujmy.

Zanim zaczniesz korzystać z jakiejkolwiek procedury lub funkcji znajdującej się w bibliotece dynamicznej,
musisz załadować bibliotekę DLL do pamięci RAM. Można załadować bibliotekę
jeden z dwóch sposobów: obciążenie statyczne i obciążenie dynamiczne.
Obie metody mają zarówno zalety, jak i wady.
Ładowanie statyczne oznacza, że ​​biblioteka dynamiczna jest ładowana automatycznie
po uruchomieniu aplikacji, która z niej korzysta. Aby skorzystać z tej metody pobierania,
musisz użyć słowa kluczowego external przy opisie eksportowanego z
funkcja lub procedura biblioteki dynamicznej. DLL jest ładowany automatycznie po uruchomieniu programu,
i będziesz mógł w ten sam sposób korzystać z wyeksportowanych z niego podprogramów
tak, jakby zostały opisane wewnątrz modułów aplikacji.
Jest to najłatwiejszy sposób wykorzystania kodu umieszczonego w bibliotece DLL.
Wadą tej metody jest to, że jeśli plik biblioteki, do której
w aplikacji jest link, nie ma go, program odmówi załadowania.
Znaczenie metody dynamicznej polega na tym, że nie ładujesz biblioteki na początku aplikacji,
i w momencie, kiedy naprawdę tego potrzebujesz. Oceń sam, bo jeśli opisana funkcja
w bibliotece dynamicznej, jest używany tylko przy 10% uruchomień programu, wtedy absolutnie nie
nie ma sensu używać metody obciążenia statycznego. W takim przypadku wyładowanie biblioteki z pamięci
jest również pod Twoją kontrolą. Kolejna zaleta tej metody
Ładowanie DLL to skrócenie (z oczywistych powodów) czasu uruchamiania aplikacji.
A jakie są wady tej metody? Wydaje mi się, że głównym z nich jest to, że zastosowanie
ta metoda jest bardziej kłopotliwa niż omówione powyżej obciążenie statyczne.
Najpierw musisz użyć funkcji API LoadLibrary Windows.
Aby uzyskać wskaźnik do eksportowanej procedury lub funkcji,
użyj funkcji GetProcAddress. Po zakończeniu korzystania z biblioteki DLL
należy pobrać za pomocą FreeLibrary.
Wywoływanie procedur i funkcji ładowanych z biblioteki DLL.
Sposób wywoływania procedur i funkcji zależy od tego, jak załadowałeś bibliotekę dynamiczną,
w którym znajdują się te podprogramy.
Wywoływanie funkcji i procedur ze statycznie ładowanych bibliotek DLL jest dość proste. Początkowo w aplikacji
musi zawierać opis eksportowanej funkcji (procedury). Potem możesz z nich korzystać
w taki sam sposób, jak gdyby zostały opisane w jednym z modułów Twojej aplikacji.
Aby zaimportować funkcję lub procedurę zawartą w bibliotece DLL, należy użyć
modyfikator zewnętrzny w ich deklaracji. Na przykład dla procedury HelloWorld, którą omówiliśmy powyżej
w aplikacji wywołującej należy umieścić następujący wiersz:
procedura SayHello(AForm: TForm); zewnętrzny plik myfirstdll.dll";
Słowo kluczowe external informuje kompilator, że procedurę można znaleźć w
biblioteka dynamiczna (w naszym przypadku myfirstdll.dll).
Wywołanie tej procedury wygląda wtedy tak:
...
Witaj Świecie(siebie);
...
Podczas importowania funkcji i procedur zachowaj szczególną ostrożność podczas pisania ich nazw i interfejsów!
Faktem jest, że w procesie kompilacji aplikacji nie jest sprawdzana poprawność nazw obiektów,
wyeksportowane z biblioteki DLL nie zostaną zaimplementowane, a jeśli błędnie opisałeś jakąkolwiek funkcję,
wtedy wyjątek zostanie zgłoszony tylko w czasie wykonywania aplikacji.
Import z biblioteki DLL może odbywać się według nazwy procedury (funkcji), numeru porządkowego lub
o innej nazwie.
W pierwszym przypadku po prostu deklarujesz nazwę procedury i bibliotekę, z której ją importujesz.
(omówiliśmy to nieco wyżej). Importowanie według numeru seryjnego wymaga podania tego właśnie numeru:
procedura HelloWorld(AForm: TForm); zewnętrzny myfirstdll.dll indeks 15;
W takim przypadku nazwa, którą nadasz procedurze importu, nie musi być taka sama jak
który został dla niego określony w samej bibliotece DLL. Tych. powyższy wpis oznacza
że importujesz z biblioteki dynamicznej myfirstdll.dll procedurę, która została w niej wyeksportowana
piętnasty, aw aplikacji ta procedura nosi nazwę SayHello.
Jeśli z jakiegoś powodu nie korzystasz z opisanej powyżej metody importu,
ale nadal chcesz zmienić nazwę importowanej funkcji (procedury), możesz użyć trzeciej metody:
procedura CoolProcedure; zewnętrzna nazwa myfirstdll.dll "DoSomethingReallyCool";
Tutaj importowana procedura CoolProcedure nosi nazwę DoSomethingReallyCool.
Wywoływanie procedur i funkcji importowanych z dynamicznie ładowanych bibliotek
nieco bardziej skomplikowana niż metoda omówiona powyżej. W takim przypadku musisz zadeklarować
wskaźnik do funkcji lub procedury, której zamierzasz użyć.
Pamiętasz procedurę HelloWorld? Zobaczmy, co należy zrobić, aby
wywołać go do wykonania w przypadku dynamicznego ładowania biblioteki DLL. Najpierw ty
konieczne jest zadeklarowanie typu opisującego tę procedurę:
rodzaj
THelloWorld = procedura(AForm: TForm);
Teraz musisz załadować bibliotekę dynamiczną, użyj GetProcAddress, aby uzyskać
wskaźnik do procedury, wywołaj tę procedurę w celu wykonania, a na koniec wyładuj bibliotekę DLL z pamięci.
Poniżej znajduje się kod pokazujący, jak można to zrobić:

Instancja DLL: THhandle ;

HelloWorld:THelloWorld;

zaczynać

(załaduj bibliotekę DLL)

(otrzymaj wskaźnik)

(wywołaj procedurę do wykonania)

Witaj Świecie(Własne ) ;

(rozładuj DLL z pamięci RAM)

FreeLibrary(DLLInstance) ;

koniec ;

Jak wspomniano powyżej, jedną z wad statycznego ładowania biblioteki DLL jest niemożność
kontynuować aplikację w przypadku braku jednej lub więcej bibliotek. W przypadku dynamicznych
wczytywanie, masz możliwość programistycznego radzenia sobie z takimi sytuacjami i uniemożliwienia programowi
wypadł sam. Zgodnie z wartościami zwracanymi przez funkcje LoadLibrary i GetProcAddress można:
określić, czy ładowanie biblioteki przebiegło pomyślnie i czy została w niej znaleziona procedura wymagana przez aplikację.
Poniższy kod pokazuje to.

procedura TForm1.DynamicLoadBtnClick (Sender: TObject ) ;

rodzaj

THelloWorld = procedura (AForm: TForm) ;

Instancja DLL: THhandle ;

HelloWorld:THelloWorld;

zaczynać

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

jeśli DLLInstance = 0 to start

WiadomośćDlg( „Nie można załadować biblioteki DLL”, mtError, [mbOK] , 0) ;

Wyjście;

koniec ;

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

jeśli @HelloWorld nil to

Witaj Świecie (Własne)

w przeciwnym razie

WiadomośćDlg( „Nie znaleziono żądanej procedury!”, mtError, [mbOK] , 0) ;

FreeLibrary(DLLInstance) ;

koniec ;

W DLL możesz przechowywać nie tylko kod, ale także formularze.
Co więcej, tworzenie i umieszczanie formularzy w bibliotece dynamicznej nie różni się zbytnio od pracy
z formularzami w zwykłym projekcie. Najpierw przyjrzymy się, jak możesz napisać bibliotekę,
zawierające formularze, a następnie porozmawiamy o wykorzystaniu technologii MDI w bibliotece DLL.
Zademonstruję rozwój biblioteki DLL zawierającej formularz na przykładzie.
Więc najpierw utwórzmy nowy projekt biblioteki dynamicznej.
Aby to zrobić, wybierz pozycję menu Plik|Nowy, a następnie kliknij dwukrotnie ikonę DLL.
Następnie zobaczysz coś takiego jak następujący kod:

Zapisz wynikowy projekt. Nazwijmy to DllForms.dpr.
Teraz musimy stworzyć nowy formularz. Można to zrobić na różne sposoby.
Na przykład wybierając pozycję menu Plik|Nowy formularz. Dodaj do formularza kilka komponentów.
Nazwijmy formularz DllForm i zapiszmy wynikową jednostkę jako DllFormUnit.pas .
Wróćmy do głównego modułu projektu i umieśćmy w nim funkcję ShowForm, której zadaniem będzie m.in.
tworzenie formularza i wyświetlanie go na ekranie. Użyj w tym celu poniższego kodu.

Formularz: TDLLFormularz;

zaczynać

Wynik:= Form.ShowModal ;

Formularz.Bezpłatny ;

koniec ;

Zwracam uwagę, że aby projekt skompilował się bez błędów, konieczne jest dodanie modułu Formularze do sekcji zastosowań.
Eksportujemy naszą funkcję za pomocą słowa kluczowego exports:
eksport
PokażFormularz;
Kompilujemy projekt i otrzymujemy plik dllforms.dll. Te proste kroki to wszystko
co należy zrobić, aby zebrać dane Zauważ, że funkcja ShowForm jest deklarowana przy użyciu słowa kluczowego stdcall.
Sygnalizuje kompilatorowi użycie konwencji podczas eksportowania funkcji
według standardowej konwencji wywoływania połączeń. Eksportowanie funkcji w ten sposób tworzy
możliwość wykorzystania opracowanej biblioteki DLL nie tylko w aplikacjach tworzonych w Delphi.
Konwencje wywoływania definiują sposób przekazywania argumentów podczas wywoływania funkcji.
Istnieje pięć podstawowych konwencji: stdcall, cdecl, pascal, register i safecall.
Więcej informacji na ten temat można znaleźć w sekcji „Konwencje połączeń” w pliku pomocy Delphi.
Zwróć też uwagę, że wartość zwracana przez funkcję ShowForm,
odpowiada wartości ShowModal. W ten sposób możesz przekazać trochę informacji
o stanie formularza do aplikacji wywołującej.
Poniżej znajdują się dwie listy, z których pierwsza zawiera pełny kod pliku
projekt DLL (moduł z formularzem nie jest tutaj pokazany), a drugi to moduł aplikacji wywołującej,
który korzysta z biblioteki, którą właśnie opracowaliśmy.

biblioteka DllForms;

używa

DllFormUnit w "DllFormUnit.pas" (DllForm) ;

($R*.RES)

funkcja ShowForm: Liczba całkowita ; standardowe wywołanie ;

Formularz: TDLLFormularz;

zaczynać

Formularz:= TDLLForm.Utwórz(Aplikacja) ;

Wynik:= Form.ShowModal ;

Formularz.Bezpłatny ;

koniec ;

zaczynać

koniec.


jednostka TestAppUnit;

interfejs

używa

Windows, wiadomości, narzędzia SysUtils, klasy, grafika,

Kontrolki, formularze, okna dialogowe, StdCtrls;

rodzaj

TForm1 = klasa(TForm)

Przycisk 1: Przycisk T;

procedura Button1Click(Sender: TObject ) ;

prywatny

(Oświadczenia prywatne)

publiczny

(Oświadczenia publiczne)

koniec ;

Form1: TForm1;

funkcja ShowForm: Liczba całkowita ; standardowe wywołanie ;

Zewnętrzny "dllforms.dll" ;

realizacja

($R *.DFM)

procedura TForm1.Button1Click (Sender: TObject ) ;

zaczynać

koniec ;

koniec.

Należy pamiętać, że słowo kluczowe stdcall zostało również użyte podczas eksportowania funkcji.
Należy zwrócić szczególną uwagę na pracę z formularzami podrzędnymi w bibliotece DLL. Jeśli na przykład
w wywołującej aplikacji formularz główny ma swoją właściwość FormStyle ustawioną na MDIForm,
wtedy przy próbie wywołania z biblioteki DLL formularza MDIChild na ekranie pojawi się komunikat o błędzie,
co powie, że nie ma aktywnego formularza MDI.
W momencie, gdy próbujesz pokazać swoje okno potomne, VCL sprawdza poprawność
właściwość FormStyle głównego formularza aplikacji. Jednak w naszym przypadku wszystko wydaje się być poprawne.
Więc o co chodzi? Problem w tym, że przy przeprowadzaniu takiej kontroli brany jest pod uwagę obiekt Aplikacji,
nie należy do aplikacji wywołującej, ale do samej biblioteki dynamicznej.
Oczywiście, ponieważ w bibliotece DLL nie ma głównego formularza, sprawdzenie daje błąd.
Aby uniknąć tej sytuacji, konieczne jest przypisanie do Application obiektu biblioteki dynamicznej
obiekt Application aplikacji wywołującej. Oczywiście zadziała to tylko wtedy, gdy
gdy program wywołujący jest aplikacją VCL. Ponadto przed wyładowaniem biblioteki z pamięci
konieczne jest przywrócenie wartości obiektu Application biblioteki do stanu pierwotnego.
Umożliwi to menedżerowi pamięci wyczyszczenie pamięci RAM zajmowanej przez bibliotekę.
Dlatego musisz przechowywać wskaźnik do natywnego obiektu Application biblioteki.
w zmiennej globalnej, której można użyć podczas przywracania jej wartości.
Cofnijmy się więc trochę i wypiszmy kroki, które musimy podjąć, aby pracować z umieszczonymi
w formularzach DLL MDIchild.
W bibliotece dynamicznej tworzymy zmienną globalną typu TApplication.
Przechowujemy wskaźnik do obiektu Application DLL w zmiennej globalnej.
Przypisujemy wskaźnik do Application do obiektu Application biblioteki dynamicznej
wywołując aplikację.
Tworzymy formularz MDIChild i pracujemy z nim.
Zwracamy wartość obiektu Application biblioteki dynamicznej do jej pierwotnego stanu
i wyładuj bibliotekę DLL z pamięci.
Pierwszy krok jest prosty. Po prostu umieść następujący kod na górze modułu DLL:
var
DllApp: TAplikacja;
Następnie tworzymy procedurę, która zmieni wartość obiektu Application i stworzymy formularz potomny.
Procedura może wyglądać mniej więcej tak:

procedura ShowMDIChild(MainApp: TApplication) ;

Dziecko: TMDIDziecko;

zaczynać

jeśli nie przypisano (DllApp), rozpocznij

Aplikacja DLL:= Aplikacja;

Aplikacja:= GłównaAplikacja;

koniec ;

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

Dziecko.Pokaż ;

koniec ;

Wszystko, co musimy teraz zrobić, to zapewnić zwrócenie wartości obiektu Application.
do stanu pierwotnego. Robimy to za pomocą procedury MyDllProc:

procedura MyDLLProc(Powód: Integer ) ;

zaczynać

jeśli powód = DLL_PROCESS_DETACH to

( DLL jest rozładowywany. Przywracanie wartości wskaźnika aplikacji)

jeśli przypisano (DllApp), to

Aplikacja:= Aplikacja DLL;

koniec ;

zamiast konkluzji.
Korzystanie z bibliotek dołączanych dynamicznie nie jest tak trudne, jak mogłoby się wydawać na pierwszy rzut oka.

Korzystanie z bibliotek dołączanych dynamicznie nie jest tak trudne, jak mogłoby się wydawać na pierwszy rzut oka.
biblioteki DLL dają najszersze możliwości optymalizacji wydajności aplikacji,
jak również praca samych programistów. Skorzystaj z DLL, a może Twoje życie stanie się prostsze!
http://subskrybuj.ru/
E-mail: [e-mail chroniony] Szukaj
do APORT na Subscribe.Ru

Niejednokrotnie trzeba było otrzymywać listy z prośbą o poinformowanie o stworzeniu i użytkowaniu DLL w Delphi. W tym artykule zajmiemy się wszystkim i stworzymy własną bibliotekę. Biblioteki łączone dynamicznie (Dinamic Link Library) umożliwiają różnym aplikacjom korzystanie w swojej pracy ze wspólnego zestawu zasobów. Ważną rzeczą jest to, że procedury i funkcje umieszczone w bibliotece DLL są wykonywane wewnątrz procesu, który ich używa. Biblioteka DLL zapewnia wszystkim aplikacjom pojedynczą kopię zasobu, która jest współużytkowana przez wszystkie aplikacje, które tego żądają, w przeciwieństwie do procedur, które uruchamiają oddzielną kopię dla każdej aplikacji, która je wywołuje. Ponadto w różnicy między bibliotekami DLL a podprogramami można uwzględnić fakt, że biblioteki DLL mogą eksportować tylko procedury i funkcje, ale nie typy, stałe itp.

Chciałbym podać fragment „Lekcji na temat Delphi” na temat struktury biblioteki DLL w pamięci:

DLL jest biblioteką, w przeciwieństwie do aplikacji, nie ma ani stosu, ani kolejki komunikatów. Funkcje umieszczone w bibliotece DLL są wykonywane w kontekście aplikacji wywołującej przy użyciu jej stosu. Ale te same funkcje wykorzystują segment danych biblioteki, a nie kopię aplikacji. Dzięki takiej organizacji biblioteki DLL oszczędności pamięci są osiągane dzięki temu, że wszystkie uruchomione aplikacje korzystają z jednego modułu DLL, nie obejmując pewnych standardowych funkcji w swoich modułach. Często w formie DLL tworzone są oddzielne zestawy funkcji, łączone według jednej lub drugiej cechy logicznej, podobnie jak moduły są koncepcyjnie planowane (w sensie jednostki) w Pascalu. Różnica polega na tym, że funkcje z modułów Pascala są łączone statycznie - na etapie łączenia, a funkcje z bibliotek DLL są łączone dynamicznie, czyli w czasie wykonywania.

Tworzenie biblioteki DLL

Struktura biblioteki DLL nie różni się zbytnio od zwykłej struktury modułu w Object Pascal. Biblioteka DLL musi zaczynać się od słowa Library, po którym następuje nazwa biblioteki. Funkcje i procedury, które biblioteka DLL będzie udostępniać (eksportować) innym użytkownikom, są wymienione po dyrektywie exports.

Dla każdej procedury lub funkcji można określić jej numer za pomocą dyrektywy Index. W przypadku braku numeru kompilator wykona automatyczne indeksowanie. Zamiast numeru procedury można użyć unikalnej nazwy, która jest określana za pomocą dyrektywy name. Jeśli nie zostanie podana nazwa funkcji ani jej numer, Delphi potraktuje to jako eksport według nazwy, która będzie pasować do nazwy funkcji.

Biblioteka może również zawierać kod inicjujący, który zostanie wykonany po załadowaniu. Znajduje się między początkiem a końcem. Oto ogólna struktura biblioteki DLL:

BibliotekaFirst_Dll; używa<используемые модули>; <объявления и описания функций>eksport<экспортируемые функции> <описание процедур и функций>zaczynać<инициализационная часть>koniec.

Podam przykłady opisu eksportowanych funkcji w dziale eksporty:

Funkcja eksportu 1 indeks 2; Nazwa funkcji2 "My_sqr"; funkcja3;

Jak można się domyślić, nazwa funkcji może nie zgadzać się z nazwą eksportu!!!

Wykorzystanie biblioteki DLL

Moduł, który musi używać procedur i funkcji z biblioteki DLL, musi używać dyrektywy zewnętrznej. Istnieją dwa sposoby korzystania z biblioteki DLL (dynamiczna i statyczna). W pierwszym przypadku aplikacja wywołująca funkcję z biblioteki DLL zna nazwę biblioteki i jej punkt wejścia i zakłada się, że biblioteka się nie zmienia. W drugim przypadku przed użyciem biblioteki DLL należy upewnić się, że wymagana biblioteka istnieje i zawiera niezbędny podprogram.

Podprogram można zaimportować według jego nazwy i numeru. Wyszukiwanie podprogramu według numeru jest szybsze, ale zawsze wygodne.

Poniżej znajdują się przykłady importowania funkcji z naszego First_DLL, który został omówiony w przykładzie:

( import według określonej nazwy ) Function ImportByName; nazwa zewnętrzna „First_DLL” „My_sqr”; ( import według indeksu ) Funkcja ImportByOrdinal; zewnętrzny indeks "First_DLL" 2; (import według oryginalnej nazwy) Funkcja Funkcja3; zewnętrzna „Pierwsza_DLL”;

Przyjrzeliśmy się statycznej metodzie korzystania z biblioteki DLL. Jednak w niektórych przypadkach nie wiadomo z góry, która biblioteka będzie potrzebna, dlatego należy zastosować metodę dynamiczną:

Używa WinType, WinProcs, ... ; wpisz TMyProc = procedura ; var Uchwyt: THhandle; MojaProc.Importowa: TMyProc; rozpocznij Handle:=LoadLibrary("FirstDLL"); if Handle>=32 to ( if<=32 - ошибка! } begin @MyImportProc:=GetProcAddress(Handle,"My_sqr"); if MyImportProc<>nil then ...... (tu używamy wynikowej funkcji) end; FreeLibrary (uchwyt); koniec;

Ale moim zdaniem wszystko, co jest tutaj napisane, nie jest zbyt jasne i chcę prawdziwych kompletnych przykładów. Zawsze byłem sfrustrowany, gdy w artykułach było niewiele przykładów, ale tylko jedna teoria, więc zwracam uwagę na prosty przykład użycia biblioteki DLL.

Kliknij w menu Plik -> Nowy i wybierz DLL. Zapisz gotowy szablon zgodnie z sugestią pod nazwą Project1.dpr.

Poniżej znajduje się jego pełny kod:

projekt biblioteczny1; korzysta z narzędzi SysUtils, klas; Funkcja Funkcja1(x,y:liczba całkowita):liczba; eksport; bginresult:=x+y; koniec; Funkcja Funkcja2(x,y:rzeczywista):rzeczywista; eksport; vart:rzeczywiste; początek t:=exp(y*ln(x)); wynik:=t; koniec; eksportuje indeks 1 funkcji 1, nazwę funkcji 2 "My_sqr"; początek koniec.

Są tu dwie funkcje, pierwsza oblicza sumę dwóch liczb i jest eksportowana według liczby, a druga oblicza x do potęgi y i jest eksportowana według nazwy.

Teraz stwórzmy nowy projekt i zapiszmy go pod inną nazwą, na przykład DemoDLL. Umieśćmy dwa przyciski na formularzu, kliknięcie pierwszego wywoła procedurę pierwszą, a kliknięcie drugiego wywoła drugą. Oto pełny kod tego projektu demonstracyjnego:

demo jednostki; interfejs wykorzystuje Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; wpisz TForm1 = class(TForm) Button1: TButton; Przycisk2: Przycisk T; procedura Button1Click(Sender: TObject); procedura Button2Click(Sender: TObject); private ( Deklaracje prywatne ) public ( Deklaracje publiczne ) koniec; var Form1: TForm1; implementacja ($R *.DFM) funkcja ImportSumm(x,y:integer):integer; zewnętrzny „Projekt1” indeks 1; //importuj według numeru funkcja ImportSqr(x,y:real):real; nazwa zewnętrzna „Projekt1” „My_sqr”; //import po nazwie procedura TForm1.Button1Click(Sender: TObject); vart:rzeczywiste; rozpocznij //Sprawdź, ile będzie dwóch do trzeciej potęgi t:=ImportSqr(2,3); Pokażwiadomość(FloatTostr(t)); koniec; procedura TForm1.Button2Click(Sender: TObject); vart:liczba całkowita; rozpocznij //Sprawdź, ile będzie 10+10 t:=ImportSumm(10,10); Pokażwiadomość(IntTostr(t)); koniec; koniec.

Programista Delphi, MySQL. Wyższa edukacja. Specjalność: oprogramowanie informatyczne.