DLL è l'abbrevizione di Dynamic Link Library (Libreria a Collegamento
Dinamico). Le DLL sono delle raccolte di routines che possono essere chiamate da più
programmi e solitamente contengono codice di utilità generale che viene messo a
disposizione di più applicazioni. Ciò permette di non inserire codice duplicato in
applicazioni differenti. Si possono assimilare le DLL alle unit di Delphi solamente che le
prime contengono codice eseguibile, collegabile in fase di esecuzione. In Delphi esistono
diversi modi di richiamare funzioni contenute in una DLL: è possibile utilizzare la
parola chiave External dopo la dichiarazione del nome della
funzione da importare, seguita dal nome del file della DLL che la contiene. Non è
necessario che la DLL sia presente in fase di compilazione, ma se questa non sarà
presente durante l'esecuzione dell'applicazione si otterrà un errore. Un esempio di
procedura importata da una DLL è il seguente
Procedure DLLProcedure1; external 'MYDLL.DLL';
Un'altro modo di richiamare una funzione o procedura contenuta in una DLL è caricando
in fase di esecuzione la libreria con l'API di windows LoadLibrary, di recuperare
l'indirizzo della procedura o funzione con l'API GetProcAddress, chiamare la procedura
attraverso l'indirizzo ottenuto ed infine, quando non occorre più, scaricare la libreria
con l'API FreeLibrary. In questo secondo caso è anche necessario dichiarare una variabile
procedura del tipo corrispondente alla procedura o funzione da richiamare. Se nell'esempio
precedente la nostra procedura avesse avuto una dichiarazione del tipo
Procedure DLLProcedure1(Param1, Param2 : Integer);
la variabile procedura avrebbe dovuto essere del tipo
Type tdLLProcedure1 = procedure(Param1, Param2 : Integer);
Var DLLProcedure1 : tdLLProcedure1;
A questa variabile deve essere assegnato il valore restituito dalla funzione
GetProcAddress nella seguente maniera
...
@DLLProcedure1 := GetProcAddress(Handle, 'DLLProcedure1');
...
dove Handle è l'handle della libreria restituito dalla chiamata a LoadLibrary.
Si consiglia di fare riferimento all'SDK di Windows per le API sopracitate.
La scrittura di una DLL in Delphi non differisce molto dalla scrittura di una normale
applicazione. La differenza sostanziale sta nella parola chiave Library che sostituisce
Program nell'intestazione della unit. In una DLL le procedure e funzioni possono essere
contenute nel file di progetto oppure contenute in unit esterne e la libreria si riduce ad
una clausola uses contente le unit con le funzioni e procedure seguita da una sezione
Exports con l'elenco delle funzioni o procedure da esportare. Il blocco principale
Begin..End di una libreria rappresenta il blocco di codice che viene eseguito come codice
di inizializzazione della libreria e viene eseguito ogni qual volta la libreria viene
caricata in memoria. Se si vogliono rendere disponibili le routines contenute in una DLL
scritta in Delphi anche ad altri linguaggi di programmazione, occorre specificare la
direttiva stdcall poichè non tutti i linguaggi supportano la modalità di chiamata
ottimizzata "register" utilizzata dall'Object Pascal.
La sezione Exports di una DLL ha la seguente struttura
exports entry1, entry2, ...,
entryn;
Ogni entry è costituita dal nome della routine da esportare (funzione o procedura)
seguita da due specificatori opzionali: index e name. Il
primo serve a definire un indice numerico per richiamare la routine esportata e può
assumere valori tra 1 e 2.147.483.647. Se per una routine non viene specificato un valore
per index gli viene assegnato automaticamente un numero tratto dalla tabella di
esportazione della DLL. Lo specificatore name è seguito da una costante
stringa e specifica il nome con cui verrà esportata la routine. Se non viene assegnato ad
una routine uno specificatore name, questa verrà esportata con il nome con cui è stata
dichiarata nella DLL, rispettando anche le maiuscole/minuscole. Tornando alla nostra
procedura di esempio, potremmo avere una sezione exports di questo genere
exports DLLProcedure1 index 1 name 'DLLProcedure1';
che è equivalente a
exports DLLProcedure1;
se vogliamo esportare la routine con un altro nome
exports DLLProcedure1 index 1 name 'DLLProcedure';
Delphi ci mette a disposizione alcune variabili globali per semplificare la gestione
delle DLL. Abbiamo visto in precedenza che il codice contenuto nel blocco principale della
libreria viene utilizzato come codice di inizializzazione della libreria stessa, ma esiste
un codice di pulizia da eseguire al rilascio della libreria? Esiste. Si può fornire
l'indirizzo di una procedura che si occuperà di eseguire il codice di pulizia tramite la
variabile ExitProc. Questa viene solitamente impostata in fase di
inizializzazione della DLL. Quando la libreria viene rilasciata, viene eseguito
automaticamente il codice contenuto nella procedura all'indirizzo contenuto in ExitProc.
Occorre però salvare il vecchio puntatore alla procedura di pulizia prima di impostare una
nuova procedura e ripristinarlo prima che la nuova procedura termini l'esecuzione.
Nella unit System è presente una variabile che permette di determinare se il codice in
esecuzione appartiene ad una DLL oppure ad un'applicazione: IsLibrary. Questa variabile
assume valore false quando si è in presenza di un'applicazione e true altrimenti.
Nella scrittura delle DLL bisogna fare attenzione ai parametri passati e restituiti
alle/dalle routines. Se si utilizzano stringhe di tipo long o array dinamici, record od
oggetti, occorre che tutti i moduli che fanno uso delle routines e le DLLs che esportano
le routines in oggetto utilizzino la unit ShareMem, dichiarandola come prima unit nella
clausola uses. Questa è una interfaccia per il gestore di memoria BORLANDMM.DLL che
permette ai moduli di condividere la memoria allocata in modo dinamico.