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.