QuestÆarticolo illustra lÆutilizzo del link dinamico delle DDL.
Salve a tutti. Ci sono molte applicazioni, in giro, in grado di estendere le proprie potenzialità attraverso appositi moduli scritti anche da terze parti. Bene, quello che stiamo per affrontare è un viaggio breve ma intenso. Ciò che vÆillustrerò è una tecnica molto interessante. Leggendo questÆarticolo, infatti, sarete in grado di scrivere applicazioni capaci di parlare una lingua qualsiasi.
EÆ possibile, infatti, tramite lÆuso di DDL scritte ad hoc, cambiare la lingua "parlata" dallÆapplicazione. Programmi scritti per lÆitaliano potranno facilmente "parlare" inglese, francese, spagnolo, ecc. Tutto senza cambiare quasi nulla di applicazioni già scritte e con poche righe aggiuntive in quelle ancora da scrivere. LÆopera di traduzione sarà semplice e potrà essere fatta da un qualsiasi programmatore, anche poco esperto, che rispetti le specifiche dellÆautore dellÆapplicazione. Iniziamo il nostro viaggio. Dovete sapere che Delphi permette il caricamento dinamico delle DDL. Questo significa che anziché assegnare unÆinterfaccia predefinita alla libreria, se ne può creare una " al volo ". Mi spiego. Ecco come appare unÆinterfaccia statica ad una Dll:
Unit DLLIntf; { Dichiara la unit di interfaccia alla DLL. }
Interface
function DoFirst( N : Integer ) : PChar;
procedure DoSecond;
implementation
function DoFirst( N : Integer ) : PChar; external æMyDllÆ name æDoFirstÆ;
procedure DoSecond;external æMyDllÆ name æDoSecondÆ;
à..
end.
Beh, questo è il metodo " classico " di interfacciare una DLL. Ma si potrebbe anche pensare ad un modo meno convenzionale di affrontare la cosa. Servono poche righe in più, ma il risultato può essere molto soddisfacente. Osservate :
Unit DynamicDLLIntf; { Dichiara la unit di interfaccia alla DLL. Utilizza un link dinamico alle
Funzioni. }
Interface
Uses Windows;
Type DoFirstFunc = function( N : Integer ) : Pchar;
DoSecondProc = procedure;
var LoadResult : ShortInt;
const LoadOk = 0;
LoadFailed = -1;
procedure InitLibrary;
function DoFirst( N : Integer ) : Pchar;
procedure DoSecond;
procedure DoneLibrary;
implementation
Var LibHandle : Thandle;
FuncFirst : DoFirstFunc;
ProcSecond : DoSecondProc;
procedure InitLibrary;
begin
LibHandle := LoadLibrary( æMyDllÆ );
if Handle = 0 then LoadResult := LoadFailed
else LoadResult := LoadOk;
end;
function DoFirst( N : Integer ) : Pchar;
begin
FuncFirst := GetProcAddress( Handle,æDoFirstFuncÆ );
if @FuncFirst = nil then LoadResult := LoadFailed
else LoadResult := LoadOk;
if ( LoadResult = LoadOk ) then
begin
Result := FuncFirst( N );
end
else Result := Pchar( æFunzione non caricata correttamenteÆ ); { Utilizza un valore di default da
controllare successivamente}
end;
procedure DoSecond;
begin
ProcSecond:= GetProcAddress( Handle,æDoSecondProcÆ );
if @ProcSecond = nil then LoadResult := LoadFailed
else LoadResult := LoadOk;
if ( LoadResult = LoadOk ) then
begin
ProcSecond;
end
else message( æRoutine non caricataÆ );
end;
procedure DoneLibrary;
begin
FreeLibrary( Handle );
end;
intialization
begin
InitLibrary;
end;
finalization
begin
DoneLibrary;
end;
end.
Ecco. Questa è una possibile implementazione di interfaccia ad una DLL in modo dinamico. Il compilatore, in sostanza, non si preoccupa di trovare gli indirizzi delle funzioni, di agganciare i riferimenti ecc. Questo dà al programmatore uno spazio di gestione notevole ed un controllo sulle
librerie dellÆapplicazione difficilmente raggiungibile in altro modo. Ovviamente ogni tecnica ha i suoi vantaggi e svantaggi. Questa in particolare ha varie controindicazioni. La prima è, ovviamente, il fattore tempo. EÆ chiaro che se le librerie utilizzate sono grandi e le funzioni sono tante, ci potrebbe essere un notevole dispendio di tempo per caricarle. Inoltre, bisogna sempre ricordarsi di liberare la memoria quando la DLL non serve più. Ed infine un altro aspetto negativo è la grandezza del codice. Ma ci sono tanti vantaggi. Supponiamo, a mero titolo esemplificativo, di voler creare un programma non solo in italiano ma anche in Inglese. Teleologicamente, quella che vi sto presentando, è unÆottima tecnica. Allegato allÆarticolo vi è il programma completo da me sviluppato, molto semplice, che attua questo tipo di tecnica. Non è prevista alcuna gestione degli errori in quel programma û anche perché non ve ne dovrebbero essere. A proposito, Ricordate SEMPRE di richiamare la routine FREELIBRARY dopo aver finito di usare le funzioni della DLL. Il rischio, infatti, è che lÆapplicazione ad ogni avvio successivo fino al restart o allo spegnimento del PC, inizi a fare le bizze dandovi dei criptici errori di runtime che sono dovuti, semplicemente, alla presenza in memoria della DLL in questione. Non dimenticatelo mai.
Ora vi propongo un poÆ di codice tratto dal programma da me realizzato, tanto per darvi una idea di ciò che ho spiegato fin qui :
unit Lang_Intf;
interface
uses Windows;
Type Caption_Text_Func = function : PChar;
procedure InitLanguage( LanguageDLL : String );
function GetFormCaption : String;
function GetButton1Caption : String;
function GetButton2Caption : String;
procedure DoneLanguage;
implementation
var LibHandle : THandle;
Capt_Func1,
Capt_Func2,
Capt_Func3 : Caption_Text_Func;
procedure InitLanguage( LanguageDLL : String );
begin
LibHandle := LoadLibrary( PChar( LanguageDLL ) );
@Capt_Func1 := GetProcAddress( LibHandle,PChar( 'GetFormCaption' ) );
@Capt_Func2 := GetProcAddress( LibHandle,PChar( 'GetButton1Caption' ) );
@Capt_Func3 := GetProcAddress( LibHandle,PChar( 'GetButton2Caption' ) );
end;
function GetFormCaption : String;
begin
Result := String( Capt_Func1 );
end;
function GetButton1Caption : String;
begin
Result := String( Capt_Func2 );
end;
function GetButton2Caption : String;
begin
Result := String( Capt_Func3 );
end;
procedure DoneLanguage;
begin
FreeLibrary( LibHandle );
end;
end.
Riferimenti bibliografici :
On Line Help di Delphi 3.
Requisiti necessari alla corretta comprensione dellÆarticolo:
Conoscenza e sviluppo di DLL per Windows.