Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Dispose = Exception, kein Dispose = Memory Leak (https://www.delphipraxis.net/99974-dispose-%3D-exception-kein-dispose-%3D-memory-leak.html)

jensw_2000 20. Sep 2007 11:05


Dispose = Exception, kein Dispose = Memory Leak
 
Liste der Anhänge anzeigen (Anzahl: 1)
Die Themen Callback und Pointer auf Records wurden hier ja schön öfter diskutiert.
Ich habe das Prinzip aber offensichtlich immer noch nicht ganz verstanden.

Folgendes Problem:

Ich fordere in der aufrufenden Prozedur mit new(PointerAufRecord) Speicher an und übergebe diesen Pointer an die empfangende Routine.
Dort muss ich den Speicher doch mit Dispose (PointerAufRecord) wieder freigeben, dachte ich ...

Dieses Prinzip verwende ich zum Datenaustausch zwischen einer Clientapplikation und einer DLL, die als API für Drittanbieter dienen soll.

Irgendwie klappt es mit dem Dispose nicht so, wie ich es gerne hätte.

Wenn ich aus der Client-Applikation eine DLL Funktion (z.B. Register_ClientApplication(aRecord:PTRecord) ) aufrufe, und dort den Speicher von TRecord mit Dispose freigebe, dann gibt es eine Exception beim Beenden des Programmes.

Wenn ich nach diesem Prinzip Daten in die Gegenrichtung (DLL > Clientanwendung) via Callback Funktion transportiere und den Speicher in der Client-Anwendung mit Dispose freigenen möchte, dann gibt es eine Exception beim Aufruf von Dispose.

Lasse ich das Dispose in der DLL und in der Anwendung weg, dann "läuft alles super". Wäre da nicht das "kleine" Problem, dass der Speicher nicht freigegeben wird .... :roll:

Ich habe ein kleines Spielprojekt angehängt.
Werft ihr bitte mal einen Blick drauf ?


Aufruf
Delphi-Quellcode:
procedure Register_API( const Servername:String );
begin
  ApiRegisterInfoRec := new(PTApiRegisterInfoRec);
  // Record füllen
  ApiRegisterInfoRec.Servername    := Servername;
  // Callback Prozeduren übergeben
  ApiRegisterInfoRec.OnState       := @OnGetApiState;
  ApiRegisterInfoRec.OnError     := @OnGetApiError;
 
  // DLL Register_ClientApplication aufrufen
  Register_ClientApplication(ApiRegisterInfoRec);
  // DLL räumt den Speichern von ApiInitRec  
end;
DLL Funktion
Delphi-Quellcode:
function Register_ClientApplication ( aApiRegisterInfo : PTApiRegisterInfoRec):bool; stdcall;
begin
  try
    // Callback Funktionen initialisieren
    @Callback_OnState          := @aApiRegisterInfo.OnState;
    @Callback_OnError          := @aApiRegisterInfo.OnError;

    // Infos aus ApiRegisterInfo übernehmen
    PipeServername                := aApiRegisterInfo.Servername;

    // Speicher von aApiRegisterInfo räumen
    // Dispose geht nicht. Exception beim Beenden der Client-Applikation
    // Dispose(aApiRegisterInfo);

    Registered := true;
  except
    on e:exception do
    begin
      registered := false;
      if assigned(Callback_OnError) then
        OnSendError(1001,esClientAPI,e.Message);
      raise exception.create('Fatal: Register_ClientApplication'+#13#10+e.Message);
    end;
  end;
  result := registered;

  // mit aktuellem Status antworten
  OnSendState(registered, true,101, PipeServername);
end;

sirius 20. Sep 2007 11:09

Re: Dispose = Exception, kein Dispose = Memory Leak
 
Achtung! du hast zwei speichermanager und kannst nicht einfach Speicher in dem einen Programm anfordern und in dem anderen freigeben.
Warum nimmst du das Dispose nicht auch in die aufrufende Exe?

Edit:
Es sollte auch so gehen (ganz ohne dispose und new)..
Aufruf:
Delphi-Quellcode:
procedure Register_API( const Servername:String );
var ApiRegisterInfoRec:TApiRegisterInfoRec;
begin
  // Record füllen
  ApiRegisterInfoRec.Servername   := Servername;
  // Callback Prozeduren übergeben
  ApiRegisterInfoRec.OnState       := @OnGetApiState;
  ApiRegisterInfoRec.OnError     := @OnGetApiError;
 
  // DLL Register_ClientApplication aufrufen
  Register_ClientApplication(@ApiRegisterInfoRec);
end;

Stefan.Buchholtz 20. Sep 2007 11:16

Re: Dispose = Exception, kein Dispose = Memory Leak
 
Wenn dynamisch belegter Speicher zwischen verschiedenen Modulen ausgetauscht wird, muss immer das Modul den Speicher freigeben, das ihn auch belegt hat. Alternativ kannst du sowohl im Programm als auch in der DLL als erstes die Unit ShareMem einbinden - das ersetzt den Standard-Speichermanager der Delphi-Runtime durch einen, der auch über Modulgrenzen funktioniert. Dann muss zusammen mit dem Programm allerdings noch die borlndmm.dll installiert werden, die diesen Speichermanager enthält.

Ohne ShareMem ist es so richtig:

Delphi-Quellcode:
procedure Register_API( const Servername:String );
begin
  ApiRegisterInfoRec := new(PTApiRegisterInfoRec);
  // Record füllen
  ApiRegisterInfoRec.Servername   := Servername;
  // Callback Prozeduren übergeben
  ApiRegisterInfoRec.OnState       := @OnGetApiState;
  ApiRegisterInfoRec.OnError     := @OnGetApiError;
 
  // DLL Register_ClientApplication aufrufen
  Register_ClientApplication(ApiRegisterInfoRec);

  // Speicher von ApiInitRec aufräumen
  Dispose(ApiRegisterInfoRec);
end;
Stefan

jensw_2000 20. Sep 2007 12:11

Re: Dispose = Exception, kein Dispose = Memory Leak
 
Ich habe jetzt das new() und Dispose() weggelassen und als Parameter "aRecord:TeinRecord" anstatt "aRecord:PTeinRecord" übergeben.

Sieht so aus als hätte ich mir das Leben zu schwehr gemacht. Es funktioniert soweit.

Sharemem will ich bewusst nicht benutzen, weil die API kompatibel mit C++ sein muss.

Danke.

Dezipaitor 20. Sep 2007 13:35

Re: Dispose = Exception, kein Dispose = Memory Leak
 
Du kannst auch LocalAlloc und LocalFree verwenden. Ich habs zwar nie wirklich direkt ausprobiert.
Aber es gibt WinApi funktionen, die rufen LocalFree oder LocalAlloc von dem Speicher auf, den ich selbst durch LocalAlloc oder LocalFree erstellt oder zerstört habe.
Die WinAPI ist ja selbst in DLLs.

sirius 20. Sep 2007 13:58

Re: Dispose = Exception, kein Dispose = Memory Leak
 
Zitat:

Zitat von Dezipaitor
Du kannst auch LocalAlloc und LocalFree verwenden. Ich habs zwar nie wirklich direkt ausprobiert.
Aber es gibt WinApi funktionen, die rufen LocalFree oder LocalAlloc von dem Speicher auf, den ich selbst durch LocalAlloc oder LocalFree erstellt oder zerstört habe.
Die WinAPI ist ja selbst in DLLs.

Das geht (oder virtualalloc...) auch. Nur sollte man sich eben überlegen, ob das wirklich nötig ist. Der Speichermanager ist ja nicht dafür da um uns das Leben schwer zu machen, sondern um das Programm zu beschleunigen. Der hängt nämlich genau zwischen new/dispose/realloc/etc. und der WinAPI (virtualalloc und virtuallfree).

Dezipaitor 20. Sep 2007 14:05

Re: Dispose = Exception, kein Dispose = Memory Leak
 
Also der beste Zweck ist bei Callback Funktionen (z.b. auch bei COM).
Wenn eine Funktion als Callback aufgerufen wird und ein Speicher erstellen soll, dann sollte er von der aufzurufende Fkt gelöscht werden.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:46 Uhr.

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