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

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

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.

Как я могу прекратить исключение после вызова метода raise, не добавляя try except / finally, где я вызываю исключение?

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


person RBA    schedule 16.12.2011    source источник
comment
Если вы собираетесь убить его еще до того, как он будет построен, зачем вам вообще его поднимать? Не используйте исключение, просто покажите вместо него окно сообщения.   -  person Sertac Akyuz    schedule 16.12.2011
comment
У меня есть приложение, которое почти на 2000 выросло, как это ... и я пытаюсь найти альтернативное решение, чтобы написать для него обработку ошибок ..   -  person RBA    schedule 16.12.2011
comment
Если коротко ответить на вопрос - вы не можете этого сделать.   -  person kludg    schedule 16.12.2011
comment
Это сделало бы сборку отладки полностью отличной от сборки выпуска, что, конечно, не подходит. Просто дайте вашему приложению обработчик исключений верхнего уровня, который обрабатывает исключения.   -  person jpfollenius    schedule 16.12.2011
comment
Это то, что я пытаюсь сделать, но иногда решения по этому поводу принимаются не мной ... поэтому мне нужно заново изобретать колесо ...   -  person RBA    schedule 16.12.2011
comment
Если в вашей программе 2000 инструкций, которых не должно быть, значит, кто-то совершил очень большую ошибку. Способ исправить эту ошибку - удалить те 2000 утверждений, которых больше не должно быть. Решение состоит в том, чтобы не изменять семантику языка, чтобы оператор raise на самом деле ничего не поднимал. Это значительно усложнит долгосрочное обслуживание вашей программы.   -  person Rob Kennedy    schedule 16.12.2011
comment
что кого-то здесь больше нет, и мне нужно решить эту неразбериху ... итак, 2000 замен, вот и я ....   -  person RBA    schedule 16.12.2011
comment
Как тихое уничтожение исключений и выполнение действий, как будто ничего не произошло (в сборке выпуска), является альтернативным решением для обработки ошибок?   -  person jpfollenius    schedule 16.12.2011
comment
@Smasher - Я думал, что часть выпуска была ошибкой, зачем показывать сообщение, если в любом случае будет вызвано исключение (часть отладки)? Опять же, может я вообще не понял ..   -  person Sertac Akyuz    schedule 16.12.2011
comment
Вы можете заключить raise операторы в {$IFDEF DEBUG}. Возможно, это было намерением.   -  person Marcus Adams    schedule 17.12.2011


Ответы (5)


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

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

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

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

person Rob Kennedy    schedule 16.12.2011

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

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

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

person David Heffernan    schedule 16.12.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;
person Igor    schedule 16.12.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;
  ..

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

person Sertac Akyuz    schedule 16.12.2011

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

Application.OnException := MainForm.DefaultExceptionHandler;

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

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

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

person jpfollenius    schedule 16.12.2011