Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Eine Variable "umziehen" (https://www.delphipraxis.net/141716-eine-variable-umziehen.html)

LDericher 14. Okt 2009 18:48


Eine Variable "umziehen"
 
Hallo DP!

Ich stehe vor folgendem Problem:
Ich arbeite an einem Parser für eine schlanke, erweiterbare Scriptsprache, und bin dabei, das Funktions/Prozedursystem zu implementieren. Dazu will ich aus einer DLL Funktionen mit einem Array of Pointer als Parameter callen. Funktionierte in den Testläufen auch wuderbar, darum geht's nicht.

Nur, wenn ich mein Script parse, will ich gleich die Pointer auf die Parameter bekommen und einlagern in ein selbtgebautes "CodeItem". Das Problem ist, wenn ich nun einen PString zwischenspeichere und dann weiterparse, dann kommt vereinfacht (Durch Schleifen) so etwas heraus:
Delphi-Quellcode:
var
  AusgabeArray: array of Pointer;
  S: string;
begin
SetLength(AusgabeArray, 2);
S:='TestEins';
AusgabeArray[0]:=@Test;
S:='Blah';
AusgabeArray[1]:=@Test;
end;
Mir ist klar, was hier schiefläuft: Der Speicherbereich von S wird ja zwischendurch nicht verändert, und so kommt letzen Endes das allmächtige "Blah" an beiden Stellen des Arrays heraus... :pale:
Kann ich irgendwie dem String/Integer/Boolean/Float/... neuen Speicherbereich zuordnen, sodas ich wirklich zwei Pointer einsortiere?

MfG, Euer LDer

himitsu 14. Okt 2009 19:04

Re: Eine Variable "umziehen"
 
Delphi-Quellcode:
SetLength(AusgabeArray, 2);
S:='TestEins';
AusgabeArray[0]:=@Test;
S:='Blah';
AusgabeArray[1]:=@Test;
also hier kann aus zwei Gründen nicht draus werden
1.) AusgabeArray[0] und AusgabeArray[1] zeigen auf die selbe Variable und haben immer den selben Inhalt
2.) S ist eine lokale Variable und würde nach ablauf der Prozedur gelöscht, also damit auch der String

was geht, ist aber das:
ein String ist ja intern ein Zeiger und ließe sich daher leicht in einen Pointer,
Delphi-Quellcode:
SetLength(AusgabeArray, 2);
S:='TestEins';
AusgabeArray[0] := Pointer(Test); // bzw. := PChar(Test);
S:='Blah';
AusgabeArray[1] := Pointer(Test);
aber hier macht einem die Referenzzählung einen Strich durch die Rechnung, da sie durch soeinen Cast nicht verändert wird und demnach der String dann wiederum freigegeben wird :stupid:

also muß man den Cast so anlegen, daß er die Referenzzählung beachtet und das ginge z.B. so
Delphi-Quellcode:
SetLength(AusgabeArray, 2);
S:='TestEins';
String(AusgabeArray[0]) := Test;
S:='Blah';
String(AusgabeArray[1]) := Test;
Delphi-Quellcode:
SetLength(AusgabeArray, 2);
String(AusgabeArray[0]) := 'TestEins';
String(AusgabeArray[1]) := 'Blah';
PS: irgendwan mußt du den String/Speicher auch wieder freigeben und das ginge dann so
Delphi-Quellcode:
String(AusgabeArray[1]) := '';


PS2: du hast dir aber auch einen Shared-MemoryManager besorgt?
denn du willst hier ja anscheinend Speicher über die Programmgrenzen hinweg verwalten/bearbeiten
und das geht nur im SELBEN speichermanager ... hier hat aber standardmäßig die EXE und die DLL jeder ihren eigenen Manager.

Es sei denn du verwendest einen WideString, welcher nicht über den Delphi-MemoryManager läuft, sondern über den vom OLE32-System.

littleDave 14. Okt 2009 19:20

Re: Eine Variable "umziehen"
 
Zitat:

Zitat von himitsu
PS2: du hast dir aber auch einen Shared-MemoryManager besorgt?
denn du willst hier ja anscheinend Speicher über die Programmgrenzen hinweg verwalten/bearbeiten
und das geht nur im SELBEN speichermanager ... hier hat aber standardmäßig die EXE und die DLL jeder ihren eigenen Manager.

Es sei denn du verwendest einen WideString, welcher nicht über den Delphi-MemoryManager läuft, sondern über den vom OLE32-System.

Man kann es auch anders machen - indem man in der DLL den Speichermanager der Hostanwendung benutzt. Das gleiche benutze ich bei meiner Script-Sprache bei den Packages auch. Im Enddefekt geht das ganz einfach:

in der DLL erstellt man so eine Funktion:
Delphi-Quellcode:
var
  oldMM : TMemoryManager;
  newMM : TMemoryManager;
  useMM : boolean;

procedure DLL_SetMemoryManager(const MemoryManager: TMemoryManager); stdcall;
begin
  if not useMM then
  begin
    // den bisherigen MemoryManager der DLL speichern
    GetMemoryManager(oldMM);
    // den neuen MemoryManager aktivieren
    newMM := MemoryManager;
    SetMemoryManager(newMM);
    // speichern, dass man den neuen MemoryManager benutzt
    useMM := True;
  end;
end;

procedure DLL_ResetMemoryManager; stdcall;
begin
  // falls ein fremder MemoryManager benutzt wurde
  if useMM then
  begin
    // den alten wieder zurücksetzen.
    SetMemoryManager(oldMM);
    useMM := False;
  end;
end;

exports
  DLL_SetMemoryManager,
  DLL_ResetMemoryManager;
Beim laden der DLL führt man dann so früh wie möglich in der Host-Anwendung die DLL-Funktion DLL_SetMemoryManager folgendermaßen aus:
Delphi-Quellcode:
var MemoryManager: TMemoryManager;
begin
  GetMemoryManager(MemoryManager);
  DLL_SetMemoryManager(MemoryManager);
end;
und beim Beenden bzw. beim Entladen der DLL ruft man einfach wieder die Funktion DLL_ResetMemoryManager der DLL auf.

Wichtig ist dabei: man sollte in der DLL kein Speicher vor dem DLL_SetMemoryManager anfordern. Zudem sollte jeder Speicher, der nach dem DLL_SetMemoryManager angefordert wurde, vor dem DLL_ResetMemoryManager wieder freigegeben werden.

Grüße

himitsu 14. Okt 2009 19:53

Re: Eine Variable "umziehen"
 
Wie man den SharedMM einrichtet ist ja egal, hauptsache man macht es, wenn man Speicher über die Grenzen hinweg verwalten will/muß.


Zitat:

Zitat von littleDave
Wichtig ist dabei: man sollte in der DLL kein Speicher vor dem DLL_SetMemoryManager anfordern. Zudem sollte jeder Speicher, der nach dem DLL_SetMemoryManager angefordert wurde, vor dem DLL_ResetMemoryManager wieder freigegeben werden.

Und genau da liegt ein großes Problem bei deiner Variante.
Sobald du z.B. die Unit SysUtils in deinem Projekt hast, wird unweigerlich der DLL-Eigene Manager schon verwendet, noch bevor du deine DLL_SetMemoryManager aufrufen kannst.

Daher wurde z.B. bei ShareMem von Borland der Manager der DLL in der EXE verwendet, da es sehr leicht möglich ist den Manager in dieser Richtung zu übergeben.

Darum ist auch mal dieser Code entstanden.
Und glaub mir, der ist schon vereinfacht ... also ich vor knapp 2 Jahren mit sowas anfing, da war der Code noch "kompliziert".
http://www.delphipraxis.net/internal...t.php?t=159601

Der Einfachste Weg ist immernoch Borlands ShareMem (borlndmm.dll)
oder man nutzt die Möglichkeiten von FastMM aus, denn dieses läßt sich auch als Shared-MM konfigurieren.

littleDave 14. Okt 2009 19:59

Re: Eine Variable "umziehen"
 
Zitat:

Zitat von himitsu
Zitat:

Zitat von littleDave
Wichtig ist dabei: man sollte in der DLL kein Speicher vor dem DLL_SetMemoryManager anfordern. Zudem sollte jeder Speicher, der nach dem DLL_SetMemoryManager angefordert wurde, vor dem DLL_ResetMemoryManager wieder freigegeben werden.

Und genau da liegt ein großes Problem bei deiner Variante.
Sobald du z.B. die Unit SysUtils in deinem Projekt hast, wird unweigerlich der DLL-Eigene Manager schon verwendet, noch bevor du deine DLL_SetMemoryManager aufrufen kannst.

So schlimm ist es nicht, da man vor dem finalization der DLL (also auch da, wo der Speicher der SysUtils wieder freigegeben wird), die Funktion DLL_ResetMemoryManager aufruft und somit den MemoryManager vom Anfang wieder als aktiv setzt.

himitsu 14. Okt 2009 20:12

Re: Eine Variable "umziehen"
 
Zitat:

Zitat von littleDave
So schlimm ist es nicht, da man vor dem finalization der DLL (also auch da, wo der Speicher der SysUtils wieder freigegeben wird), die Funktion DLL_ResetMemoryManager aufruft und somit den MemoryManager vom Anfang wieder als aktiv setzt.

Da würde ich an deiner Stelle nicht so einfach und blind drauf vertauen.

z.B. rufe mal die Funktion Delphi-Referenz durchsuchenLanguages aus der Unit SysUtils auf.
Diese nutzt intern ein Objekt, welches nicht schon in der Initialisierung der Unit erstellt wird, sondern erst beim ersten Aufruf der Funktion, allerdings wird dieses Objekt erst in der Finalisierung der Unit freigegeben
und da hast du dem Objekt schon seinen Speichermanager gemopst, in welchem es erstellt wurde.

Und da gibt es noch mehrere Stellen, wo soetwas passiert.
z.B. Application der Unit Forms ist da auch ein netter Fall
und dann gibt es noch mehr in Classes, Graphics und vorallem die Indy's sind da nette Gegenspieler
usw.


Also wenn man da in seinem eigenem Programm die Kontrolle darüber behält, was wo und vorallem wann erstellt/freigegeben wird, dann kann man es sich schon einfach machen,

Aber wenn du z.B. sowas wie deine Script-Sprache auch mal an andere Programmierer weitergeben willst, wo du absolut keinen Einfluß auf das Speicherverhalten hast, dann sollte man schon etwas aufpassen.


[add]
PS: Aus diesen Gründen wird bei meiner Version auch (1.) die DLL dynamisch geladen, damit sich der Speichermanager der EXE noch vor dem der DLL initialisieren kann.
Und (2.) der Manager innerhalb der DLL wird noch vor "allen" anderen Units innerhalb der DLL gestartet/übernommen, also noch von der Initialisierung von z.B. SysUtils und nach derern Finalisierung wird der MM auch erst wieder an das System (die EXE) zurückgegeben.

Und das 2. Verhalten ist auch bei allen Shared-MemoryManagern gleich.
1. ist bei mir nur umgekehrt, da ich ja den MM von der EXE auf die DLL übertragen möchte.
Möchte man den MM von der DLL in der EXE nutzen, so wie es sonst alle anderen Shared-MMs machen, dann muß die DLL statisch vor der EXE geladen werden.

LDericher 15. Okt 2009 12:08

Re: Eine Variable "umziehen"
 
Tag,

Hab jetzt einfach die Methode von littledave reingenommen, da ich meine DLLs auch dynamisch lade. Bis jetzt sieht's gut aus, und himitsu hat mir auch sehr geholfen :)

Also: :dp: und erledigt!

Bei weiteren Fragen/Problemen reaktiviere ich das Topic mal ;)

Gruß,
LDer.

himitsu 15. Okt 2009 12:53

Re: Eine Variable "umziehen"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hier mal ein winziges Beispiel, mit littleDaves Code:

Also die Exceptions sprechen doch für sich?
Es wird da dem Languages-Objekt und einem String einfach so der Manager unterm Arsch weggemopst.

PS: wegen dem IntToStr nicht wundern, aber dieses stellt ganz einfach nur eine "echte" Stringvariable bereit ... String-Konstanten sind ja nicht im MM abgelegt.

LDericher 15. Okt 2009 14:27

Re: Eine Variable "umziehen"
 
Okay, ich verstehe das Problem, aber für mich dürfte es hier nicht relevant sein, da ich nur mit Parametern abeite, bzw. den dazugehörenden Prozeduren ;)
Gut, ich speichere halt den MM zwischen, aber ich hatte noch keine solche Exceptions. Auch im Initialization/Finalization-Teil mache ich nix, genauso wenig wie zwischen Begin und End der DLL.

himitsu 15. Okt 2009 14:53

Re: Eine Variable "umziehen"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von LDericher
da ich nur mit Parametern abeite, bzw. den dazugehörenden Prozeduren ;)

Du nutzt also keinerlei Funktionen/Prozeduren, welche dir Delphi zur Verfügung stellt?
Also das nenne ich mal richtiges Non(V)CL :thumb:

Zitat:

Zitat von LDericher
Auch im Initialization/Finalization-Teil mache ich nix, genauso wenig wie zwischen Begin und End der DLL.

Och, wenn's nur darum geht ... die kann man auch weglassen (siehe Anhang)

Ich dachte nur je mehr Exceptions, desto besser. :lol:

Es geht ja nicht nur darum, daß du da nichts machst, denn da könnte man eingreifen und dieses selber rechtzeitig behandeln.
Aber es gibt genügend andere Units (im angehängten Beispiel ist es die böse SysUtils)


Nja, wie gesagt, wenn man schon fremden Code (und dazu gehört auch Delphi CL und VCL) nutzt, dann sollte man schon ein bissl aufpassen ... aber mann kann auch Glück haben und zufällig nur Funktionen erwischen, wo dennoch alles gut geht :angel:


PS: bei eigenen Programmen ist es ja nicht so schlimm, wenn nur diese abstürzen,
dann ist ja nur das eigene Programm betroffen und eventuell reagieren nur die eigenen Kunden sauer.
Aber stell dir mal vor du baust eine Komponente, welche andere in ihrem Programm verwenden ... diese bringt dann abundzu mal in fremden Anwendungen Probleme ... Wenn das Problem dann wissentlich eingebaut war, dann könnte das eine schöne Klagewelle erzeugen :?


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:34 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz