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/)
-   -   AcquireExceptionObject() / ReleaseExceptionObject() (https://www.delphipraxis.net/192895-acquireexceptionobject-releaseexceptionobject.html)

Der schöne Günther 31. Mai 2017 08:07

Delphi-Version: 10 Seattle

AcquireExceptionObject() / ReleaseExceptionObject()
 
Ich bin jetzt ein bisschen baff :shock:

Wenn ich möchte dass eine Exception über ihren
Delphi-Quellcode:
try..except
-Block hinaus lebt gibt es zwei Routinen AcquireExceptionObject() und ReleaseExceptionObject().

Schaut man in den interface-Teil von System.pas:
Zitat:

{
When an exception is thrown, the exception object that is thrown is destroyed
automatically when the except clause which handles the exception is exited.
There are some cases in which an application may wish to acquire the thrown
object and keep it alive after the except clause is exited. For this purpose,
we have added the AcquireExceptionObject and ReleaseExceptionObject functions.
These functions maintain a reference count on the most current exception object,
allowing applications to legitimately obtain references. If the reference count
for an exception that is being thrown is positive when the except clause is exited,
then the thrown object is not destroyed by the RTL, but assumed to be in control
of the application. It is then the application's responsibility to destroy the
thrown object. If the reference count is zero, then the RTL will destroy the
thrown object when the except clause is exited.
}
function AcquireExceptionObject: Pointer;
procedure ReleaseExceptionObject;

Schaut man in den implementation-Teil dann gibt es
Delphi-Quellcode:
ReleaseExceptionObject()
zwei mal:

Delphi-Quellcode:
procedure ReleaseExceptionObject;
begin
end;
und
Delphi-Quellcode:
procedure ReleaseExceptionObject;
asm
{$IFDEF ALIGN_STACK}
        SUB    ESP, 12
{$ENDIF ALIGN_STACK}
        CALL   CurrentException
{$IFDEF ALIGN_STACK}
        ADD    ESP, 12
{$ENDIF ALIGN_STACK}
        OR     EAX, EAX
        JE     @@Error
        CMP    [EAX].TRaisedException.RefCount, 0
        JE     @@Error
        DEC    [EAX].TRaisedException.RefCount
        RET
@@Error:
{ This happens if there is no exception pending, or
  if the reference count on a pending exception is
  zero. }
        JMP  _Run0Error
end;
Aufgerufen wird natürlich ersteres. Heißt: Nichts passiert, die Exception verbleibt auf ewig im Speicher.

Kann mir jemand den Sinn dahinter erklären? wtf...

himitsu 31. Mai 2017 08:52

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Es gibt normal auch keine Referenzzählung (ARC), also was soll es da machen?
ReleaseExceptionObject ist doch eh nur dazu da, um das Exceptionobjekt vor dem except-end freigeben zu wollen. Gibt doch kaum Gründe, wozu man das unbedingt braucht.

Der schöne Günther 31. Mai 2017 09:02

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Da ich nicht riechen kann was intern im Programm abläuft, wie es Resourcen in einem try..except-Block verwaltet kann ich nur auf was vertrauen was in der Doku geschrieben steht und evtl. noch was im RTL-Quelltext für mich sichtbar ist.

Gründe eine Exception länger leben zu lassen kann ich haben, finde ich.

Und für mich sieht es momentan völlig falsch aus - Warum sollte man die Methode mit einem leeren Gegenstück doppeln? Ist das in neueren Delphi-Versionen immer noch so?

himitsu 31. Mai 2017 10:08

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1373049)
Gründe eine Exception länger leben zu lassen kann ich haben, finde ich.

Das schon und AcquireExceptionObject funktioniert ja auch (jedenfalls in meinen Programmen)
und wenn ReleaseExceptionObject nichts macht, dann lebt die Exception-Instanz halt noch ein kleines bissl länger, als gewollt. (bis zum END vom Except)

Zitat:

Zitat von Der schöne Günther (Beitrag 1373049)
Ist das in neueren Delphi-Versionen immer noch so?

Ach, an so altem Zeug ändert sich doch fast nie was. (wird immer nur Neues eingeaut und die alten Bugs behalten :zwinker:)

Jupp, ist noch so.

Der schöne Günther 31. Mai 2017 14:34

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Zitat:

Zitat von himitsu (Beitrag 1373064)
und wenn ReleaseExceptionObject nichts macht, dann lebt die Exception-Instanz halt noch ein kleines bissl länger, als gewollt. (bis zum END vom Except)

Nein, sie lebt bis zum Tag des Jüngsten Gerichts. Und der Tag kommt an dem Punkt an dem der Anwendung der Speicher ausgeht, deshalb ist es mir erst aufgefallen. Denn normalerweise vertraue ich bei einem mehrere tausend Euro teuren Produkt darauf dass zumindest die Basics funktionieren.

himitsu 31. Mai 2017 15:13

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Hier werden ständig so viele Exceptions ausgelöst,
wenn die nicht freigegeben würden (im END), dann würde bei uns doch auch ständig der Speicher ausgehn. :gruebel:

Aktuell noch Delphi XE, aber hier sieht der Code gleich aus.

Fritzew 31. Mai 2017 15:36

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Habe mal mit Berlin einen kleinen Test geschrieben:

Delphi-Quellcode:
procedure ttestexception.Test1;
var p : Pointer;
begin
  try
   Writeln('Test 1');

   raise etestexception.Create;
  except
   Writeln('Exceptblock 1');
   Writeln('AcquireExceptionObject');
   p := AcquireExceptionObject;
   ReleaseExceptionObject;
  end;
 Writeln('Leave Test1');

end;

procedure ttestexception.Test2;
var p : Pointer;
begin
  try
   writeln;
    Writeln('Test 2');

   raise etestexception.Create;
  except
   Writeln('Exceptblock 2');
  end;
 Writeln('Leave Test2');

end;

{ EtestException }

constructor EtestException.create;
begin
  inherited create('Test');
  writeln('Create EtestException ');
end;

destructor EtestException.Destroy;
begin
  writeln('Free EtestException ');
  inherited;
end;
Result:
Code:
Test 1
Create EtestException
Exceptblock 1
AcquireExceptionObject
Leave Test1

Test 2
Create EtestException
Exceptblock 2
Free EtestException
Leave Test2
Done
Wird also nicht freigegeben

Der schöne Günther 31. Mai 2017 15:44

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Danke für den Test.

Mein Test wäre jetzt gewesen es in einer Schleife aufzurufen und in den Task-Manager zu schauen wie sich der Speicher füllt. Das ist natürlich schlauer :thumb:

himitsu 31. Mai 2017 16:12

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Hatte ich doch gesagt
* ReleaseExceptionObject macht halt nichts, wie schon richtig gesehn, aber sooooo sehr stört es jetzt nicht
* aber AcquireExceptionObject funktioniert dennoch (wäre blöd, wenn nicht)

Hatte das auch grade mal schnell als Test gebastelt:
Delphi-Quellcode:
type
  EMyTest = class(Exception)
  public
    // TObject
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    class function NewInstance: TObject; override;
    procedure FreeInstance; override;
    //destructor Destroy; override;
  protected
    // Exception
    procedure RaisingException(P: PExceptionRecord); override;
  public
    // Exception
    constructor Create(const Msg: string);
    destructor Destroy; override;
  end;

procedure EMyTest.AfterConstruction;
begin
  //Form5.Memo1.Lines.Add('Exception.AfterConstruction');
  inherited;
end;

procedure EMyTest.BeforeDestruction;
begin
  //Form5.Memo1.Lines.Add('Exception.BeforeDestruction');
  inherited;
end;

constructor EMyTest.Create(const Msg: string);
begin
  Form5.Memo1.Lines.Add('Exception.Create ' + Msg);
  inherited;
end;

destructor EMyTest.Destroy;
begin
  Form5.Memo1.Lines.Add('Exception.Destroy ' + Message);
  inherited;
end;

procedure EMyTest.FreeInstance;
begin
  //Form5.Memo1.Lines.Add('Exception.FreeInstance');
  inherited;
end;

class function EMyTest.NewInstance: TObject;
begin
  //Form5.Memo1.Lines.Add('Exception.NewInstance');
  Result := inherited;
end;

procedure EMyTest.RaisingException(P: PExceptionRecord);
begin
  Form5.Memo1.Lines.Add('Exception.RaisingException');
  inherited;
end;

procedure TForm5.FormCreate(Sender: TObject);
var
  E: {Exception}TObject;
begin
  {$REGION 'normal'}
  Memo1.Lines.Add('');
  Memo1.Lines.Add('***** normal');
  try
    Memo1.Lines.Add('raise Exception.Create()');
    //raise Exception.Create('Fehlermeldung');
    raise EMyTest.Create('Fehlermeldung');
    Memo1.Lines.Add('nö');
  except
    Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('end');
  end;
  Memo1.Lines.Add('finish');
  {$ENDREGION}

  {$REGION 'release'}
  Memo1.Lines.Add('');
  Memo1.Lines.Add('***** release');
  try
    Memo1.Lines.Add('raise Exception.Create()');
    //raise Exception.Create('Fehlermeldung');
    raise EMyTest.Create('Fehlermeldung');
    Memo1.Lines.Add('nö');
  except
    Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('ReleaseExceptionObject');
    ReleaseExceptionObject;
    Memo1.Lines.Add('end');
  end;
  Memo1.Lines.Add('finish');
  {$ENDREGION}

  {$REGION 'acquire'}
  Memo1.Lines.Add('');
  Memo1.Lines.Add('***** acquire');
  try
    Memo1.Lines.Add('raise Exception.Create()');
    //raise Exception.Create('Fehlermeldung');
    raise EMyTest.Create('Fehlermeldung');
    Memo1.Lines.Add('nö');
  except
    Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('AcquireExceptionObject');
    E := AcquireExceptionObject;
    Memo1.Lines.Add('end');
  end;
  Memo1.Lines.Add('E.Free');
  E.Free;
  Memo1.Lines.Add('finish');
  {$ENDREGION}

  {$REGION 're-raise'}
  Memo1.Lines.Add('');
  Memo1.Lines.Add('***** re-raise');
  try
    try
      Memo1.Lines.Add('raise Exception.Create()');
      //raise Exception.Create('Fehlermeldung');
      raise EMyTest.Create('Fehlermeldung');
      Memo1.Lines.Add('nö');
    except
      Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
      Memo1.Lines.Add('raise E');
      raise;
      Memo1.Lines.Add('end');
    end;
    Memo1.Lines.Add('finish');
  except
    Memo1.Lines.Add('except(2) ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('end(2)');
  end;
  Memo1.Lines.Add('finish(2)');
  {$ENDREGION}

  {$REGION 'new-raise'}
  Memo1.Lines.Add('');
  Memo1.Lines.Add('***** new-raise');
  try
    try
      Memo1.Lines.Add('raise Exception.Create(1)');
      //raise Exception.Create('Fehlermeldung_1');
      raise EMyTest.Create('Fehlermeldung_1');
      Memo1.Lines.Add('nö');
    except
      Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
      Memo1.Lines.Add('raise Exception.Create(2)');
      raise EMyTest.Create('Fehlermeldung_2');
      Memo1.Lines.Add('end');
    end;
    Memo1.Lines.Add('finish');
  except
    Memo1.Lines.Add('except(2) ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('end(2)');
  end;
  Memo1.Lines.Add('finish(2)');
  {$ENDREGION}


  {$REGION 'acquire + re-raise'}
  Memo1.Lines.Add('');
  Memo1.Lines.Add('***** acquire + re-raise');
  try
    Memo1.Lines.Add('raise Exception.Create()');
    //raise Exception.Create('Fehlermeldung');
    raise EMyTest.Create('Fehlermeldung');
    Memo1.Lines.Add('nö');
  except
    Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('AcquireExceptionObject');
    E := AcquireExceptionObject;
    Memo1.Lines.Add('end');
  end;
  try
    raise E;
  except
    Memo1.Lines.Add('except ' + Exception(ExceptObject).Message);
    Memo1.Lines.Add('end');
  end;
  Memo1.Lines.Add('finish');
  {$ENDREGION}
end;
Code:
***** normal
raise Exception.Create()
Exception.Create Fehlermeldung
Exception.RaisingException
except Fehlermeldung
end
Exception.Destroy Fehlermeldung
finish

***** release
raise Exception.Create()
Exception.Create Fehlermeldung
Exception.RaisingException
except Fehlermeldung
ReleaseExceptionObject
end
Exception.Destroy Fehlermeldung
finish

***** acquire
raise Exception.Create()
Exception.Create Fehlermeldung
Exception.RaisingException
except Fehlermeldung
AcquireExceptionObject
end
E.Free
Exception.Destroy Fehlermeldung
finish

***** re-raise
raise Exception.Create()
Exception.Create Fehlermeldung
Exception.RaisingException
except Fehlermeldung
raise E
except(2) Fehlermeldung
end(2)
Exception.Destroy Fehlermeldung
finish(2)

***** new-raise
raise Exception.Create(1)
Exception.Create Fehlermeldung_1
Exception.RaisingException
except Fehlermeldung_1
raise Exception.Create(2)
Exception.Create Fehlermeldung_2
Exception.RaisingException
Exception.Destroy Fehlermeldung_1
except(2) Fehlermeldung_2
end(2)
Exception.Destroy Fehlermeldung_2
finish(2)

***** acquire + re-raise
raise Exception.Create()
Exception.Create Fehlermeldung
Exception.RaisingException
except Fehlermeldung
AcquireExceptionObject
end
Exception.RaisingException
except Fehlermeldung
end
Exception.Destroy Fehlermeldung
finish
Tja, das kaputte ReleaseExceptionObject müsste ja eigentlich so aussehn, wenn es funktionieren würde:
Code:
***** release
raise Exception.Create()
Exception.Create Fehlermeldung
Exception.RaisingException
except Fehlermeldung
ReleaseExceptionObject
Exception.Destroy Fehlermeldung <<<<<<<<<<<<<<<<<
end
finish
anstatt
Code:
...
ReleaseExceptionObject
end
Exception.Destroy Fehlermeldung <<<<<<<<<<<<<<<<<
finish

Der schöne Günther 1. Jun 2017 12:10

AW: AcquireExceptionObject() / ReleaseExceptionObject()
 
Vielleicht möchten alle die können voten:

https://quality.embarcadero.com/browse/RSP-18259


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:05 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