Подтвердить что ты не робот

Как мне повторно поднять исключение Delphi после его регистрации?

Знаете ли вы способ блокирования, регистрации и повторного создания исключения в коде 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;

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

4b9b3361

Ответ 1

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

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;

Ответ 2

Следуя за запиской Craig Young, я использовал что-то в соответствии с следующим кодом. Вы можете сохранить исходное местоположение исключения, используя идентификатор "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;

Ответ 3

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

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

.

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

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

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

Ответ 4

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

function AcquireExceptionObject: Pointer;

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

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

Ответ 5

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

begin
  MyHandleException(E);
  Raise;
end;

Ответ 6

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

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;

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

Ответ 7

Вы можете получить объект исключения до вызова обработчика и оставить обработчик самим одним лайнером. Однако у вас все еще есть много бремени "Try/Except/Do/End".

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 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 возвращает значение (действует как 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 handler
  End);
End;

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

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 handler
  End);
End;

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