Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Wann werden Exception-Objekte freigegeben? (https://www.delphipraxis.net/139930-wann-werden-exception-objekte-freigegeben.html)

himitsu 8. Sep 2009 20:33


Wann werden Exception-Objekte freigegeben?
 
Mal so rein aus Interesse. (hab meinen Code nun doch anders gestaltet)

Wann werden diese Objekte denn eigentlich freigegeben?


Bis grad eben hatte ich noch Folgendes verbaut:
Delphi-Quellcode:
Class Procedure TXHelper.HandleException(E: Exception; Proc: TXMLExceptionEvent; Owner: TObject);
  Var B: Boolean;

  Begin
    B := False;
    Try
      If Assigned(Proc) Then Proc(Owner, E, B);
    Except
      On E2: Exception do Begin
        E := E2;
        B := True;
      End;
    End;
    If B Then ShowException(E, nil);
  End;
Aufgerufen wurde/wird es bei mir so:
Delphi-Quellcode:
Try
  ...
Except
  On E: Exception do TXHelper.HandleException(E, _OnException, Self);
End;
Ich konnte diesen Code bis jetzt noch nicht testen (da das Gesamtprojekt noch nicht wieder lauffähig ist)

Wäre es da eventuell zu Problemen gekommen?

Also E ist ja noch innerhalb seiner Except-Blocks,
aber E2 wäre ja außerhalb gewesen.


PS: jetzt sieht mein Code so aus:
Delphi-Quellcode:
Class Procedure TXHelper.HandleException(E: Exception; Proc: TXMLExceptionEvent; Owner: TObject);
  Var B: Boolean;

  Begin
    Try
      B := False;
      If Assigned(Proc) Then Proc(OwnerE, B);
      If B Then ShowException(E, nil);
    Except
      On E2: Exception do ShowException(E2, nil);
    End;
  End;

littleDave 8. Sep 2009 21:19

Re: Wann werden Exception-Objekte freigegeben?
 
Gerade ausprobiert:
Delphi-Quellcode:
type
  EMyException = class(Exception)
  public
    destructor Destroy; override;
  end;

destructor EMyException.Destroy;
begin
  ShowMessage('Destroy');
  inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    raise EMyException.Create('Test');
  except
    on e:Exception do
      ShowMessage('OnExcept');
  end;
  ShowMessage('End');
end;
Reihenfolge: OnExcept -> Destroy -> End

himitsu 8. Sep 2009 21:44

Re: Wann werden Exception-Objekte freigegeben?
 
Auf die Idee, es so leicht zu prüfen war ich jetzt garnicht gekommen :oops:

Hatte nur keine große Lust es zu debuggen und hatte die Hoffnung es weiß schon einer :nerd:

Hab es gleich nochmal rekursiv probiert.
Delphi-Quellcode:
destructor EMyException.Destroy;
begin
  ShowMessage('Destroy: ' + Message);
  inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    try
      raise EMyException.Create('Test 1');
    except
      on E: Exception do begin
        ShowMessage('OnExcept: ' + E.Message);
        raise EMyException.Create('Test 2');
      end;
    end;
  except
    on E: Exception do
      ShowMessage('OnExcept: ' + E.Message); {X}
  end;
  ShowMessage('End');
end;
Es wird also immer gleich nach seiner letzen Verwendung freigegeben.

Und hier demnach erst nach dem 2. Try-Except-Block.
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    try
      raise EMyException.Create('Test 1');
    except
      on E: Exception do begin
        ShowMessage('OnExcept: ' + E.Message);
        raise;
      end;
    end;
  except
    on E: Exception do
      ShowMessage('OnExcept: ' + E.Message);
  end;
  ShowMessage('End');
end;

Nja, es hätte ja sein können, daß bis alle Objekte mindestens bis zur letzen Except-Ebene aufgehoben werden
und man somit eine Art Backtacking hätte durchführen können, um so alle nachfolgenden(vorhergehenden) Exception rauszubekommen.


Also daß man z.B. an Punkt {X} auch noch von der Exception 'Test 1' hätte etwas erfahren können.

Jedenfalls hätte ich hier durch das RaiseListPtr soetwas vermutet. :gruebel:
Delphi-Quellcode:
{Unit System}
function ExceptObject: TObject;
begin
  if RaiseListPtr <> nil then
    Result := PRaiseFrame(RaiseListPtr)^.ExceptObject
  else
    Result := nil;
end;

Apollonius 9. Sep 2009 19:31

Re: Wann werden Exception-Objekte freigegeben?
 
Himitsu, ich kann das gerade wirklich nicht glauben. Ich hätte gewettet, dass du Delphi-Referenz durchsuchenAcquireExceptionObject kennst. Bezüglich Freigabe der Exception ohne genannte Funktion schaue man in System.pas:
Delphi-Quellcode:
//_HandleAnyException - selbiges in _HandleOnException
        JMP    EBX //zum Handler springen

@@exceptFinally:
        JMP    _HandleFinallyInternal

@@destroyExcept:
        {       we come here if an exception handler has thrown yet another exception }
        {       we need to destroy the exception object and pop the raise list. }

        CALL   SysInit.@GetTLS
        MOV    ECX,[EAX].RaiseListPtr
        MOV    EDX,[ECX].TRaiseFrame.NextRaise
        MOV    [EAX].RaiseListPtr,EDX

        MOV    EAX,[ECX].TRaiseFrame.ExceptObject
        JMP    TObject.Free
Sobald eine Exception also einen Except-Block verlässt (Reraise offensichtlich ausgenommen), wird die alte Exception freigegeben, AcquireExceptionObject natürlich ausgenommen.
Die von dir angesprochene Raiselist ist in einem anderen Fall wichtig:
Delphi-Quellcode:
try
  raise EFirstException.Create('');
except
  //die aktuelle Exception ist eine EFirstException
  try
    raise ESecondException.Create('');
  except
    //die aktuelle Exception ist eine ESecondException
  end;
  //die ESecondException ist zerstört worden: die EFirstException ist jetzt aktuell
end;

himitsu 9. Sep 2009 19:57

Re: Wann werden Exception-Objekte freigegeben?
 
Nee, das hat sich über die Jahre erfolgreich vor mir versteckt :shock:

Hmmm, diese Art der Verschachtelung kommt bei mir recht selten vor.

Aktuell sieht es bei mir so aus ... bei einem Re-Raise wird die alte Exception der Neuen im Constructor mitgegeben und die Messages werden samt Klassen- und Funktionsnamen miteinander verbunden.
Delphi-Quellcode:
Type EXMLException = Class(Exception)
     _Info: Array of Record
       ErrorClass:   TClass;
       FunctionsName: String;
       Message:      String;
     End;
     Const MaxXMLErrStr = 200;
     Class Function Str(S: AnsiString;   MaxLen: Integer = MaxXMLErrStr): String; Overload;
     Class Function Str(S: UnicodeString; MaxLen: Integer = MaxXMLErrStr): String; Overload;
     Constructor Create(ErrorClass: TClass; Const FunctionsName: String;
       ResStringRec: PResStringRec; Const Args: Array of Const;
       PrevException: Exception = nil; ErrorCode: LongWord = ERROR_SUCCESS); Overload;
     Constructor Create(ErrorClass: TClass; Const FunctionsName: String;
       ResStringRec: PResStringRec; Const S: UnicodeString); Overload;
     Constructor Create(ErrorClass: TClass; Const FunctionsName: String;
       ResStringRec: PResStringRec; i: Integer = 0); Overload;
  End;

Class Function EXMLException.Str(S: AnsiString; MaxLen: Integer = MaxXMLErrStr): String;
  Const Width: Array[Boolean] of Byte = (2, 4);

  Var i: Integer;

  Begin
    If MaxLen < 0 Then Begin
      If Length(S) > -MaxLen Then S := Copy(S, 1, -MaxLen);
    End Else
      If Length(S) > MaxLen Then S := Copy(S, 1, MaxLen) + '...';
    For i := Length(S) downto 1 do
      If Ord(S[i]) in [0{..9, 11, 12, 14.}..31, 34, 127, 160, 255] Then Begin
        Insert(AnsiString(Format('%.*x', [Width[(i < Length(S))
          and (Ord(S[i + 1]) in [Ord('0')..Ord('9'), Ord('A')..Ord('F'),
            Ord('a')..Ord('f')])], Ord(S[i])])), S, i + 1);
        S[i] := '#';
      End;
    Result := String(S);
  End;

Class Function EXMLException.Str(S: UnicodeString; MaxLen: Integer = MaxXMLErrStr): String;
  Const Width: Array[Boolean] of Byte = (2, 4);

  Var i: Integer;

  Begin
    If MaxLen < 0 Then Begin
      If Length(S) > -MaxLen Then S := Copy(S, 1, -MaxLen);
    End Else
      If Length(S) > MaxLen Then S := Copy(S, 1, MaxLen) + '...';
    For i := Length(S) downto 1 do
      If ((SizeOf(Char) = 1) and (Ord(S[i]) > 255))
          or (Ord(S[i]) in [0{..9, 11, 12, 14.}..31, 34, 127, 160, 255]) Then Begin
        Insert(UnicodeString(Format('%.*x', [Width[(i < Length(S))
          and (Ord(S[i + 1]) in [Ord('0')..Ord('9'), Ord('A')..Ord('F'),
            Ord('a')..Ord('f')])], Ord(S[i])])), S, i + 1);
        S[i] := '#';
      End;
    Result := String(S);
  End;

Constructor EXMLException.Create(ErrorClass: TClass; Const FunctionsName: String; ResStringRec: PResStringRec;
    Const Args: Array of Const; PrevException: Exception = nil; ErrorCode: LongWord = ERROR_SUCCESS);

  Var i: Integer;

  Begin
    If PrevException is EXMLException Then
      _Info := Copy(EXMLException(PrevException)._Info)
    Else If PrevException is Exception Then Begin
      SetLength(_Info, 1);
      _Info[0].ErrorClass   := ErrorClass;
      _Info[0].FunctionsName := '-';
      _Info[0].Message      := Exception(ErrorClass).Message;
    End;
    If ErrorCode <> ERROR_SUCCESS Then Begin
      i := Length(_Info);
      SetLength(_Info, i + 1);
      _Info[i].ErrorClass   := nil;
      _Info[i].FunctionsName := 'System';
      _Info[i].Message      := SysErrorMessage(ErrorCode);
    End;
    i := Length(_Info);
    SetLength(_Info, i + 1);
    _Info[i].ErrorClass   := ErrorClass;
    _Info[i].FunctionsName := FunctionsName;
    _Info[i].Message      := Format(LoadResString(ResStringRec), Args);
    Message := Format('[%s] ' + '%s.%s:' + sLineBreak + '%s', [ClassName,
      _Info[i].ErrorClass.ClassName, _Info[i].FunctionsName, _Info[i].Message]);
    While (i > 0) and (Length(Message) < 3000) do Begin
      Dec(i);
      Message := Format('%s' + sLineBreak + sLineBreak + '%s.%s:' + sLineBreak + '%s', [Message,
        _Info[i].ErrorClass.ClassName, _Info[i].FunctionsName, _Info[i].Message]);
    End;
    If Length(Message) > 3500 Then Message := Copy(Message, 1, 3497) + '...';
  End;

Constructor EXMLException.Create(ErrorClass: TClass; Const FunctionsName: String;
    ResStringRec: PResStringRec; Const S: UnicodeString);
  Begin
    Create(ErrorClass, FunctionsName, ResStringRec, [Str(S)]);
  End;

Constructor EXMLException.Create(ErrorClass: TClass; Const FunctionsName: String;
    ResStringRec: PResStringRec; i: Integer = 0);
  Begin
    Create(ErrorClass, FunctionsName, ResStringRec, [i]);
  End;
und aufrufen tu ich's dann etwa so
Delphi-Quellcode:
Procedure TXMLFile.SetLineFeed(Const Value: UnicodeString);
  Begin
    If ... Then
      Raise EXMLException.Create(Self.ClassType, 'LineFeed', @SInvalidValue, Value);
    _LineFeed := Value;
  End;

Apollonius 9. Sep 2009 20:11

Re: Wann werden Exception-Objekte freigegeben?
 
Warum holst du dir nicht mit AcquireExceptionObject die alte Exception und speicherst sie als Feld in der neuen Exception?

himitsu 9. Sep 2009 20:37

Re: Wann werden Exception-Objekte freigegeben?
 
Dann müßte ich es ja schonwieder umbauen und mir vorher noch überlegen, wie ich die SystemErrorMessages (GetLastError) dort mit unterbekomm

Apollonius 9. Sep 2009 20:38

Re: Wann werden Exception-Objekte freigegeben?
 
Das eine schließt das andere nicht aus. Ich finde es einfach ganz nett, noch ein InnerException-Objekt zu haben. Freigabe nicht vergessen.

himitsu 9. Sep 2009 20:44

Re: Wann werden Exception-Objekte freigegeben?
 
Zitat:

Zitat von Apollonius
Das eine schließt das andere nicht aus. Ich finde es einfach ganz nett, noch ein InnerException-Objekt zu haben. Freigabe nicht vergessen.

Am Ende hab ich dann eine Exception in einer Exception in einer Exception.
Im Grunde reichen mir aber die Texte und Namen, so daß ich mir jetzt nicht unbedingt alle Exceptions aufheben muß.
Wenn die aber eh aufgehoben würden, dann hätte ich mir die Texte und Namen jedesmal dort rausgeholt.

Also ich wüßte jetzt keinen Grund mir die extra selber noch aufzuheben. :gruebel:

Apollonius 9. Sep 2009 20:58

Re: Wann werden Exception-Objekte freigegeben?
 
Da ist wohl der .NET-Gaul mit mir durchgegangen. Da in Delphi der Callstack nicht im Exception-Objekt gespeichert wird, gibt es keinen Grund zur Aufbewahrung, das stimmt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:05 Uhr.

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