![]() |
Delphi-Version: 10 Seattle
AcquireExceptionObject() / ReleaseExceptionObject()
Ich bin jetzt ein bisschen baff :shock:
Wenn ich möchte dass eine Exception über ihren
Delphi-Quellcode:
-Block hinaus lebt gibt es zwei Routinen
try..except
![]() ![]() Schaut man in den interface-Teil von System.pas: Zitat:
Schaut man in den implementation-Teil dann gibt es
Delphi-Quellcode:
zwei mal:
ReleaseExceptionObject()
Delphi-Quellcode:
und
procedure ReleaseExceptionObject;
begin end;
Delphi-Quellcode:
Aufgerufen wird natürlich ersteres. Heißt: Nichts passiert, die Exception verbleibt auf ewig im Speicher.
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; Kann mir jemand den Sinn dahinter erklären? wtf... |
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. |
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? |
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Zitat:
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:
Jupp, ist noch so. |
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Zitat:
|
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. |
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Habe mal mit Berlin einen kleinen Test geschrieben:
Delphi-Quellcode:
Result:
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;
Code:
Wird also nicht freigegeben
Test 1
Create EtestException Exceptblock 1 AcquireExceptionObject Leave Test1 Test 2 Create EtestException Exceptblock 2 Free EtestException Leave Test2 Done |
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: |
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:
Tja, das kaputte ReleaseExceptionObject müsste ja eigentlich so aussehn, wenn es funktionieren würde:
***** 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
Code:
anstatt
***** release
raise Exception.Create() Exception.Create Fehlermeldung Exception.RaisingException except Fehlermeldung ReleaseExceptionObject Exception.Destroy Fehlermeldung <<<<<<<<<<<<<<<<< end finish
Code:
...
ReleaseExceptionObject end Exception.Destroy Fehlermeldung <<<<<<<<<<<<<<<<< finish |
AW: AcquireExceptionObject() / ReleaseExceptionObject()
|
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Zitat:
AcquireExceptionObject hängt die Exception von dem Exception-Block ab und gibt sie dir und ReleaseExceptionObject würde die Exception des Exception-Block freigeben, welche es da aber schon nicht mehr gibt. rein logisch sind diese beiden Codes identisch
Delphi-Quellcode:
=
except ReleaseExceptionObject; end;
Delphi-Quellcode:
except AcquireExceptionObject.Free; end;
|
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Das ist ja nur ein Test. Der Klassische Fall das Exception-Objekt außerhalb des try..except-Blocks weiterleben lassen zu wollen ist es bspw. in einem anderen Thread zu verarbeiten (Exception-Logging, ...).
So macht das
Delphi-Quellcode:
auch damit man nach Thread-Ende noch das Feld
TThread
Delphi-Quellcode:
auslesen kann.
FatalException
Und ja, ich weiß dass ich das zurückgegebene TObject / Exception "normal" freigeben kann. Es geht aber darum dass die Doku ein Laufzeitverhalten verspricht das überhaupt nicht existiert. Dass Code existiert der anscheinend überhaupt nicht aktiv ist da er von einer leeren Prozedur verdeckt wird. |
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Es lassen sich nicht alle Dinge auf allen Platformen gleich umsetzen und das ist eine plaformübergreifende Funktion.
Wenn es in einer Platform kein äquivalentes Verhalten gibt und sich das Standardverhalten nicht extrem wiedersprüchlich verhalten würde, dann macht man dort einfach nichts und schon ist gut. :angle: Da finde ich es "schwachsinniger", dass .Free unter ARC garnichts macht, obwohl man dort etwas genau jetzt freigeben will. |
AW: AcquireExceptionObject() / ReleaseExceptionObject()
Die Doku is kompletter Unfug. ReleaseExceptionObject kann gar nicht das machen, was da steht, weil man AcquireExceptionObject so oft und auf so viele Exceptions machen kann, wie man will. Wenn man sie freigeben möchte, reicht ein einfaches Free bzw auf nil setzen der Variable unter ARC (ab Delphi 10.2).
Vorher war die Funktion unter ARC noch verbuggt, da sie einen Pointer zurücklieferte und somit ARC umging (siehe ![]() Korrektur: Das mit dem RefCount in ReleaseExceptionObject ist nur unter dem IFDEF PC_MAPPED_EXCEPTIONS und hat mit ARC nix zu tun - guckstu: ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:53 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