Delphi - обработка исключений в собственном конструкторе после его вызова - PullRequest
4 голосов
/ 16 декабря 2011

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

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TMyErrorClass = class(Exception)
    constructor Create(aMsg:String);
  end;
  TForm2 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormCreate(Sender: TObject);
begin
//
 raise TMyErrorClass.Create('test');
end;

{ TMyErrorClass}

constructor TMyErrorClass.Create(aMsg: String);
begin
 {$IFDEF DEBUG}
   Showmessage(aMsg);
 {$ELSE}
  //here I want to 'kill' the exception
 {$ENDIF}
end;

end.

После того, как рейз вызван, как я могу прекратить Исключение, не добавляя try, за исключением / наконец, где я поднимаю исключение?

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

Ответы [ 5 ]

5 голосов
/ 16 декабря 2011

После того как вы ввели оператор raise, есть только два способа избежать возникновения этого исключения:

  • Сначала поднимите что-нибудь еще. Из конструктора исключения, которое должно быть вызвано, вы можете сначала создать и вызвать другое исключение. Это позволит вашей программе не достичь первого оператора raise.

  • Завершить поток, чтобы ничто не запустилось. Вы можете сделать это различными способами, включая ExitThread, Halt и ExitProcess, которые все в значительной степени эквивалентны в этом контексте. Ваша программа больше не будет работать, но, по крайней мере, вы не позволили ей вызвать это исключение!

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

3 голосов
/ 16 декабря 2011

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

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

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

1 голос
/ 16 декабря 2011

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

 
procedure TestException(dwExceptionCode, dwExceptionFlags,
    nNumberOfArguments: DWORD; lpArguments: PDWORD); stdcall;
var
  Arg: array of DWORD absolute lpArguments;
begin
{$IFDEF DEBUG}
  if (nNumberOfArguments > 1) and Assigned(lpArguments) and
      (TObject(Arg[1]).ClassName = TMyErrorClass.ClassName) then begin
    ShowMessage('Test');
    TObject(Arg[1]).Free;
    Exit;
  end;
{$ENDIF}
  windows.RaiseException(dwExceptionCode, dwExceptionFlags,
                            nNumberOfArguments, lpArguments);

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RaiseExceptionProc := @TestException;
  ..

Протестировано только с 32-битным и D2007понятия не имею, будет ли это работать иначе.

1 голос
/ 16 декабря 2011

Когда создается экземпляр TMyErrorClass, он не знает, будет ли он raised или нет. Вы просите разорвать причинно-следственную связь чего-то такого же фундаментального, как обработка языковых исключений. Я никогда не буду помещать это в производственный код, потому что это будет бомба замедленного действия. Наслаждайтесь (написано в Delphi 6):

type
  TMyClass = class(TObject)
  public
    Name: string;
    constructor Create;
  end;

type
  REArguments = array[0..100] of DWORD;
  PREArguments = ^REArguments;

type
  TRaiseExceptionProc = procedure (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PREArguments); stdcall;

var
  RaiseExceptionProc_Old: TRaiseExceptionProc;

procedure MyRaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PREArguments); stdcall;
var
  raisedObject: TObject;
begin
  if @RaiseExceptionProc_Old <> nil then
  begin
    RaiseExceptionProc := @RaiseExceptionProc_Old;
    RaiseExceptionProc_Old := nil;

    raisedObject := Pointer(lpArguments^[1]);
    if raisedObject is TMyClass then
    begin
      raisedObject.Free;
    end
    else
    begin
      TRaiseExceptionProc(RaiseExceptionProc)(dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
    end;
  end;
end;

constructor TMyClass.Create;
begin
  inherited;
  Name := 'MyClass';
  RaiseExceptionProc_Old := RaiseExceptionProc;
  RaiseExceptionProc := @MyRaiseException;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  raise TMyClass.Create;
  ShowMessage('Continue without interruption.');
end;
0 голосов
/ 16 декабря 2011

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

Application.OnException := MainForm.DefaultExceptionHandler;

procedure TMainForm.DefaultExceptionHandler (Sender : TObject; E : Exception);

begin
{$IfDef DEBUG}
ShowMessage (E.Message);
{$EndIf}
end;

Продолжение нормального потока управления после исключения - плохая идея, ИМХО.

...