How should I re-raise a Delphi exception after logging it?

前端 未结 8 1248
攒了一身酷
攒了一身酷 2021-01-01 10:40

Do you know a way to trap, log, and re-raise exception in Delphi code? A simple example:

procedure TForm3.Button1Click(Sender: TObject);
begin
  try
    rais         


        
相关标签:
8条回答
  • 2021-01-01 11:09

    You should be able to just use the Raise command by itself to re-raise the exception:

    begin
      MyHandleException(E);
      Raise;
    end;
    
    0 讨论(0)
  • 2021-01-01 11:11

    This way was working for me!

    procedure RaiseExceptionFmt(const AFormat: string;
      const AArgs: array of const);
    begin
      raise Exception.CreateFmt(AFormat, AArgs) at ExceptAddr;
    end;
    

    I rewrote my method, and now the raise will be the same before.

    procedure RaiseInternalExceptionFmt(const AFormat: string;
      const AArgs: array of const);
    var
      LExceptionPtr: Pointer;
    begin
      LExceptionPtr := AcquireExceptionObject();
      try
        Exception(LExceptionPtr).Message := Format(AFormat, AArgs);
        raise Exception(LExceptionPtr) at ExceptAddr;
      finally
        ReleaseExceptionObject();
      end;
    end;
    
    0 讨论(0)
  • 2021-01-01 11:20

    You could acquire the exception object before calling your handler and keep the handler itself one liner. However, you still have a lot of "Try/Except/Do/End" burden.

    Procedure MyExceptionHandler(AException: Exception);
    Begin
      Log(AException); // assuming it accepts an exception
      ShowMessage(AException.Message);
      raise AException; // the ref count will be leveled if you always raise it
    End;
    
    Procedure TForm3.Button1Click(Sender: TObject);
    Begin
      Try
        Foo;
      Except On E:Exception Do
        MyExceptionHandler(Exception(AcquireExceptionObject));
      End;
    End;
    

    However, if what you only want to do is to get rid of repetitive error handling code in event handlers, you could try this:

    Procedure TForm3.ShowException(AProc : TProc);
    Begin
      Try
        AProc;
      Except On E:Exception Do Begin
        Log(E);
        ShowMessage(E.Message);
      End; End;
    End;
    

    Reducing your event handler code to this:

    Procedure TForm3.Button1Click(Sender: TObject);
    Begin
      ShowException(Procedure Begin // anon method
        Foo; // if this call raises an exception, it will be handled by ShowException's handler
      End);
    End;
    

    You can also make it work for functions, using parametrized functions:

    Function TForm3.ShowException<T>(AFunc : TFunc<T>) : T;
    Begin
      Try
        Result := AFunc;
      Except On E:Exception Do Begin
        Log(E);
        ShowMessage(E.Message);
      End; End;
    End;
    

    And making ShowException return a value (acting as a passthru):

    Procedure TForm3.Button1Click(Sender: TObject);
    Var
      V : Integer;
    Begin
      V := ShowException<Integer>(Function : Integer Begin // anon method
        Result := Foo; // if this call raises an exception, it will be handled by ShowException's handler
      End);
    End;
    

    Or even making the anon procedure touch directly the outer scope variable(s):

    Procedure TForm3.Button1Click(Sender: TObject);
    Var
      V : Integer;
    Begin
      ShowException(Procedure Begin // anon method
        V := Foo; // if this call raises an exception, it will be handled by ShowException's handler
      End);
    End;
    

    There are some limitations on the interaction of variables from inside the body of the anonymous function and the ones defined in the outer scope, but for simple cases like these, you will be more than fine.

    0 讨论(0)
  • 2021-01-01 11:25

    If you want to re-raise the exception only under certain conditions, write

    procedure TForm3.Button1Click(Sender: TObject);
    begin
      try
        raise Exception.Create('Bum');
      except
        on E: Exception do
        begin
          if MyHandleException(E) then
            raise;
        end;
      end;
    end;
    
    function TForm3.MyHandleException(AException: Exception): boolean;
    begin
      ShowMessage(AException.Message);
      result := true/false;
    end;
    
    0 讨论(0)
  • 2021-01-01 11:25

    You could try to use (system.pas):

    function AcquireExceptionObject: Pointer;
    

    AcquireExceptionObject returns a pointer to the current exception object and prevents the exception object from being deallocated when the current exception handler exits.

    Note: AcquireExceptionObject increments the exception object's reference count. Make sure that the reference count is decremented when the exception object is no longer needed. This happens automatically if you use the exception object to re-raise the exception. In all other cases, every call to AcquireExceptionObject must have a matching call to ReleaseExceptionObject. AcquireExceptionObject/ReleaseExceptionObject sequences can be nested.

    0 讨论(0)
  • 2021-01-01 11:27

    Following on from Craig Young's post, I've used something along the lines of the following code successfully. You can preserve the original exception location by using the "at" identifier with the ExceptAddr function. The original exception class type and information is also preserved.

    procedure MyHandleException(AMethod: string);
    var
      e: Exception;
    begin
      e := Exception(AcquireExceptionObject);
      e.Message := e.Message + ' raised in ' + AMethod; 
      raise e at ExceptAddr;
    end;
    
    try
      ...
    except
      MyHandleException('MyMethod');
    end;
    
    0 讨论(0)
提交回复
热议问题