1. Co to są biblioteki DLL?
W Microsoft® Windows®, dynamicznie dołączane biblioteki (Dynamic Load
Library) są modułami, które zawierają funkcje i dane. Biblioteka DLL jest
ładowana w czasie wykonywania procesu, wywołanego z poziomu innych modułów (.EXE lub
.DLL). Po załadowaniu, biblioteka jest odwzorowywana w przestrzeń adresową wywołanego
procesu.
Biblioteki DLL mogą zawierać dwa rodzaje funkcji: eksportowane i wewnętrzne. Funkcje
eksportowane mogą być wywoływane z innych modułów. Natomiast funkcje wewnętrzne
mogą być wywoływane tylko w bibliotece, w której zostały zdefiniowane. Pomimo, że
biblioteki DLL mogą eksportować dane, to zazwyczaj, dane znajdujące się w bibliotekach
są używane tylko lokalnie.
Stosowanie bibliotek DLL pozwala na modułowe tworzenie aplikacji. Wiąże się to z
możliwością łatwej modernizacji funkcjonalności programów. Pomaga też w redukowaniu
zapotrzebowania na pamięć, szczególnie wtedy, gdy kilka aplikacji korzysta
jednocześnie z takiej samej bazy funkcji. Pomimo, że każda aplikacja tworzy własną
kopię danych, to oszczędność pamięci wynika z możliwości uwspólniania kodu.
W Microsoft® Win32® API (Application Programming Interface) zaimplementowano zbiór
dynamicznie łączonych bibliotek, więc procesy używające Win32 API używają
połączeń dynamicznych.
(text by Arnold Adamczyk, Katedra Miernictwa Elektronicznego, Wydział ETI,
Politechnika Gdańska)
2. Różnice między "unitem" a biblioteką DLL.
Główna różnica to sposób dołączania kodu do programu. Kod wykonywalny biblioteki DLL jest umieszczany tylko jeden raz w PAO. Wszystkie programy korzystajace z biblioteki DLL odwołują się do tego samego miejsca w pamięci. Gdy programy korzystają z unitów, każdy z nich ma swoją statyczną kopię, co w efekcie niepotrzebnie zajmuje PAO.
3. Jak utworzyć bibliotekę DLL w Delphi?
Robi się to tak samo jak przy tworzeniu zwykłych aplikacji.
Trzeba wybrać: File -> New -> DLL, potem Project -> Options ->
VersionInfo -> Module Attributes i tam trzeba zaznaczyć DLL.
Składnia jest podobna do składni unitu. Ale dla formalności wygląda tak:
library <nazwa>; //musi być taka sama jak nazwa pliku!!!
Uses //deklaracje używanych unitów
SysUtils, Classes;
//deklaracje zmiennych, deklaracje i definicje funkcji
exports
//poniżej jest miejsce na deklaracje funkcji udostępnianych w DLL'u
//np.
//my_funct_l index 1,
//my_function_2 index 2;
begin
//część inicjalizująca
end.
Indeks to liczba porządkowa, potrzebna przy importowaniu funkcji z biblioteki do
programu.
Przy deklaracji funkcji & procedur należy pamiętać o dyrektywie stdcall,
która decyduje o sposobie przekazywania parametrów do wywoływanych funkcji/procedur z
DLL'a. Stdcall jest standardowym sposobem przekazywania parametrów w Windozie, więc
polecane jest ją stosować. Dlaczego? Chodzi m.in. o kompatybilność między kodami
napisanymi w różnych językach. Przykład: Napisałem DLL'a w C++ i zaimportowałem z
niego funkcje do programu napisanego w Delphi. Na początku przy deklaracjach funkcji
pominąłem dyrektywę stdcall. Efekt był piorunujący: BLUE SCREEN! YEA!. Bo oczywiście
C++ przekazuje parametry do funkcji od prawej do lewej, a Delphi odwrotnie (albo na
odwrót - can't remember). I stąd całe zamieszanie.
Pamiętajcie zatem - przy pisaniu DLL'a najlepiej skompilować go tak, ażeby wszystkie funkcje wywoływane były z dyrektywą stdcall. Przy importowaniu funkcji nie zapomnijcie również tej dyrektywy dodać. A robi się to tak:
function My_function(...): ...; stdcall;
procedure my_proc(...);stdcall;
Żeby otrzymać wynikowego DLL trzeba tylko skompilować aktualny "projekt".
4. Jak zaimportować funkcje & procedury z DLL 'a?
Jest kilka sposobów. Główny podział to importowanie dynamiczne i statyczne.
Importowane statyczne polega na pobieraniu zawsze tej samej procedury z tej samej
biblioteki. Importowanie dynamiczne polega na określeniu biblioteki dopiero w trakcie
wykonywania programu. Aby zaimportować dynamiczne trzeba zadeklarować użycie unitu
Windows.
Okej, poniżej opisałem sposoby importowania funkcji (i procedur też - ale mnie to
wkurza):
a) Importowanie dynamiczne przez liczbę porządkową (moje ulubione :) )
Jest to dokładnie tak, jak na załączonym przykładzie. Trzeba najpierw zadeklarować
identyfikator
biblioteki (tzw. handler) czyli np. var xxx: HModule. Żeby móc użyć typu Hmodule
trzeba najpierw zadeklarować użycie unitu Windows. Teraz konieczna jest deklaracja danej
funkcji/procedury (posłużę się jednym przykładem):
rob_cos : procedure (...); stdcall;
Następnie trzeba załadować DLL' a do PAO:
xxx:=LoadLibrary (<nazwa>);
<nazwa> to obiekt typu string.
Kolejnym krokiem jest zaimportowanie interesującej nas funkcji. Robi się to za
pomocą procedury GetProcAddress(handler: Hmodule, Pchar(liczba_porządkowa) );
Np. (zakładam, że liczba porządkowa dla rob_cos to 1)
Rob cos:=GetProcAddress(xxx,Pchar(1) );
No i teraz hulaj dusza. Można już korzystać z funkcji rob_cos. Nie zapomnijcie tylko zwolnić pamięci po zakończeniu programu:
FreeLibrary(<nazwa>);
b) Importowanie statyczne poprzez nazwę
Deklaracje funkcji w programie muszą wyglądać następująco:
function haha(...) : zwrot; external 'moj_dll' name 'nazwa_funkcji_w_dll'; stdcall;
Czyli odnosząc sie do załączonego przykładu taka deklaracja wyglądałaby następująco:
function odejmij(const a, b : TWektor) : TWektor; external 'dll_sample' name 'odejmij_wektory'; stdcall;
c) Importowanie statyczne poprzez liczbę porządkową
Ogólnie deklaracja taka ma postać:
function rehehe(...) : typ ; external 'moj_dll' index liczba; stdcall;
I znowu odniosę się do załączonego przykładu:
function odejmij(const a, b :TWektor) : TWektor; external 'dll_sample' index 2; stdcall;
opracował:
Adam Grześko