Как мне повторно вызвать исключение Delphi после его регистрации? - PullRequest
25 голосов
/ 27 мая 2010

Знаете ли вы, как перехватывать, регистрировать и повторно вызывать исключения в коде Delphi? Простой пример:

procedure TForm3.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('Bum');
  except
    on E: Exception do
    begin
      MyHandleException(E);
    end;
  end;
end;

procedure TForm3.MyHandleException(AException: Exception);
begin
  ShowMessage(AException.Message);
  LogThis(AException.Message);  
  // raise AException; - this will access violate
end;

Так что мне нужно повторно вызвать его в блоке исключений, но мне было интересно, есть ли лучший способ написать свой собственный метод для обработки и (при определенных условиях) повторный вызов исключений.

Ответы [ 8 ]

31 голосов
/ 27 мая 2010

Если вы хотите повторно вызвать исключение только при определенных условиях, напишите

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;
9 голосов
/ 03 мая 2012

Исходя из сообщения Крейга Янга, я успешно использовал что-то вроде следующего кода. Вы можете сохранить исходное местоположение исключения, используя идентификатор «at» с функцией ExceptAddr. Исходный тип класса исключений и информация также сохраняются.

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;
6 голосов
/ 06 апреля 2011

Следующее будет работать, но, конечно, не идеально по 2 причинам:

  • Исключение возникает из другого места в стеке вызовов.
  • Вы не получите точную копию исключения - особенно те классы, которые добавляют атрибуты. То есть вам придется явно скопировать нужные вам атрибуты.
  • Копирование пользовательских атрибутов может быть затруднено из-за обязательной проверки типа.

.

procedure TForm3.MyHandleException(AException: Exception);
begin
  ShowMessage(AException.Message);
  LogThis(AException.Message);  
  raise ExceptClass(AException.ClassType).Create(AException.Message);
end;

Преимущество заключается в том, что вы сохраняете исходный класс исключений и сообщение (и любые другие атрибуты, которые вы хотите скопировать).

В идеале вы хотели бы вызвать System._RaiseAgain, но, увы, это «магическая компиляторная» процедура, которая может быть вызвана только raise;.

4 голосов
/ 28 мая 2010

Вы можете попробовать использовать (system.pas):

function AcquireExceptionObject: Pointer;

AcquireExceptionObject возвращает указатель на текущий объект исключения и предотвращает освобождение объекта исключения при выходе из текущего обработчика исключения.

Примечание: AcquireExceptionObject увеличивает счетчик ссылок объекта исключения. Убедитесь, что счетчик ссылок уменьшается, когда объект исключения больше не нужен. Это происходит автоматически, если вы используете объект исключения для повторного вызова исключения. Во всех других случаях каждый вызов AcquireExceptionObject должен иметь соответствующий вызов ReleaseExceptionObject. Последовательности AcquireExceptionObject / ReleaseExceptionObject могут быть вложенными.

2 голосов
/ 27 мая 2010

Вы можете просто использовать команду Raise для повторного вызова исключения:

begin
  MyHandleException(E);
  Raise;
end;
1 голос
/ 13 ноября 2018

Этот способ работал для меня!

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

Я переписал свой метод, и теперь повышение будет таким же, как раньше.

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;
1 голос
/ 21 мая 2018

Старая тема, но как насчет этого решения?

procedure MyHandleException(AException: Exception);
begin
  ShowMessage(AException.Message);
  AcquireExceptionObject;
  raise AException;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('Bum');
  except
    on E: Exception do
      MyHandleException(E);
  end;
end;

Оно основано на первом коде, опубликованном Eduardo.

0 голосов
/ 13 марта 2016

Вы можете получить объект исключения перед вызовом вашего обработчика и оставить один обработчик самим обработчиком. Тем не менее, у вас по-прежнему много бремени «попробовать / исключить / сделать / закончить».

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;

Однако, если вам нужно только избавиться от повторяющегося кода обработки ошибок в обработчиках событий, вы можете попробовать это:

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

Сокращение вашего кода обработчика событий до этого:

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;

Вы также можете заставить его работать для функций, используя параметризованные функции:

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;

И заставить ShowException возвращать значение (действующее как промежуточный):

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;

Или даже заставить процедуру anon напрямую коснуться внешней переменной (ей) области действия:

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;

Существуют некоторые ограничения на взаимодействие переменных внутри тела анонимной функции и тех, которые определены во внешней области видимости, но для простых случаев, подобных этим, вам будет более чем хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...