Seite 1 von 1

Lokale Variablen in DLLs

Verfasst: Dienstag 14. Oktober 2008, 12:09
von abacom
ulliban



Anmeldungsdatum: 09.08.2007
Beiträge: 22
Wohnort: Gross Ippener
Verfasst am: 04.03.2008 12:22 Titel: Lokale Variablen in DLLs

--------------------------------------------------------------------------------

Werden in einer DLL globale Variablen deklariert, so kommt es in PL zu gegenseitigem Überschreiben der Inhalte dieser Variablen, wenn mehrere Instanzen der DLL geladen werden.

Nach meinem Verständnis der Funktion einer DLL dürfte dies eigentlich gar nicht der Fall sein, weswegen ich glaube, dass PL nicht wirklich MEHRERE Instanzen der DLL lädt sondern in der Realität nur EINE Instanz der DLL lädt und immer PHYSIKALISCH DIESELBEN Funktionen/Prozeduren mit Pointern auf UNTERSCHIEDLICHE Speicherbereiche für Input, Output und User aufruft.

Aber egal, ob meine Vermutung nun richtig oder falsch ist: Umgehen mit der Tatsache muss man auf jeden Fall. Der PL-Ansatz zu diesem Problem ist der Speicherbereich, auf den PUser^ zeigt und der als privat für jede geladene "Instanz" der DLL gelten darf.

Zwar kann man mit geeigneter Typ-Umwandlung allerlei zaubern und in diesem Array of Extended allerlei unterbringen, was man im ersten Moment gar nicht vermutet, aber irgendwo sind dieser Typfriemelei natürlich Grenzen gesetzt. Das haben die Menschen, die z.B. mit Strings umgehen wollen, auch bemerkt und in einem anderen Thread die Frage diskutiert, wie man sich über einen Trick dann doch wieder "lokale" Strings herstellen kann. Dies hat mich auf die Idee gebracht, diesen Trick so zu verallgemeinern, dass sich die DLL mit geringstem Aufwand lokale Variablen fast beliebigen Typs vorhalten kann.

Das ganze geht so:

Alle lokalen Variablen, die man benötigt, packt man in die Typdeklaration eines Records, also z.B.

Code:

Type

TLocalVars= Record
LastAbfrage:TDateTime;
JetztZeit:TDateTime;
com32:T_QCCom32;
RecString:String;
DatenListe:TStringList;
end;


Beachten Sie, dass hier im Beispiel mit einer TSTringList und T_QCCom32 (einer Delphi-Freeware-Komponente zur Verwaltung serieller Schnittstellen) komplexe Objekte benutzt werden, die nicht mehr auf ein "Array of Extended" abbildbar wären.

Die DLL erhält sodann EINE globale Variable:

Code:
Var

LocalVars: Array of TLocalVars;


Nun schreibt man sich noch zwei Hilfsprozeduren zum Verwalten der lokalen Variablen:
Code:

Procedure CheckLocalAndCreate(PUser: PDLLParams);
var DLL:Integer;
begin;
DLL:=Round(PUser^[DLLIndex]);
if DLL>High(LocalVars) then SetLength(LocalVars,DLL+1);
With LocalVars[DLL] do
begin;
DatenListe:=TSTringList.Create;
Com32:=T_QCCom32.Create(Nil);
Com32.Name:='Com'+inttostr(DLL);
Com32.ShowErrors:=False;
Com32.Port:=Round(Puser^[ComPort]);
Com32.Baud:=Round(PUser^[ComBaud]);
Com32.Databits:=Round(PUser^[ComBits]);
Com32.Stopbits:=Round(PUser^[ComStop]);
Com32.Parity:=Char(Round(PUser^[ComPar]));
Com32.Open;
end;
end;

Procedure CheckLocalAndDestroy(PUser: PDLLParams);
var i,DLL:Integer;
begin;
DLL:=Round(PUser^[DLLIndex]);
With LocalVars[DLL] do
begin;
com32.Close;
com32.Free;
For i:=Datenliste.Count-1 downto 0 do DatenListe.Objects.Free;
DatenListe.Free;
end;
end;


In "CheckLocalAndCreate" wird zunächst getestet, ob das dynamische Array für die gegenwärtig aktive "Instanz" der DLL groß genug ist und gegebenenfalls seine Größe inkrementiert. Damit ist ein Satz neuer lokaler Variablen unter diesem Array-Index verfügbar und man kann daran gehen, die dynamischen darunter zu erschaffen und ihnen Defaults zuzuordnen. Das ist der Teil innerhalb des "With LocalVars[DLL] do"-Blocks.

Ganz analog dazu dient "CheckLocalAndDestroy" zum Aufräumen dynamisch angelegter Dinge. Um sich einen Satz lokaler Variablen zu verschaffen ruft man lediglich die beiden Routinen in den Start- bzw. Stopprozeduren auf:

Code:

Procedure SimStart(PInput,POutput,PUser: PDLLParams);
begin
CheckLocalAndCreate(PUser);
end;

Procedure SimStop(PInput,POutput,PUser: PDLLParams);
begin
CheckLocalAndDestroy(PUser);
end;

Procedure Calculate(PInput,POutput,PUser: PDLLParams);
var DLL: Integer;
begin
DLL:=Round(PUser^[DLLIndex]);
With LocalVars[DLL] do
begin;
// Hier steht der eigentliche Code, z.B.:
RecString:='';
end;
end;


Die Prozeduren und Funktionen, die man aus "Calculate" heraus sonst noch aufrufen möchte, richtet man praktischerweise so ein, dass immer PUser übergeben wird, so dass jede Prozedur mit

Code:

DLL:=Round(PUser^[DLLIndex]);
With LocalVars[DLL] do
begin;


das De-Referenzieren des lokalen Variablenblocks hinbekommen kann. Eigentlich alles trivial, man muß nur drauf kommen...

MfG
Ulrich Bangert