Правилно третиране на катастрофални грешки

Непрекъснато се натъквам на нещо, което наистина не съм разрешил с програми на Delphi и се чудех дали някой може да ме инструктира за това. Както се казва в темата, как се прави правилното обработване на катастрофални грешки? Например:

// is file necessary for the program present?
if not FileExists(FilePath1) then
   begin
     raise Exception.Create(FilePath1 + ' does not exist and is required for this program to function.');
   // I obviously need to do something here to make the program QUIT and not have
   // any more code run.

     Application.Terminate;
     Abort;
   end;

Мога да използвам модула за изключение и там и да изхвърля изключение, но програмата продължава както преди. Използвал съм обаждането за спиране в миналото, но изглежда, че не извършва никакво почистване или други подобни, така че в крайна сметка правя голяма процедура с близки и безплатни обаждания за всичко, което съм направил, само за да съм сигурен (и дори тогава аз не съм сигурен в нищо от задкулисните неща).

И така, какъв е правилният начин за справяне с такива неща?

Редактиране: За да изясня, искам да знам как да накарам програмата да направи каквото почистване трябва да направи и след това да ИЗЛЕЗНЕ СЕГА и да не изпълнява друг код.


person Glenn1234    schedule 20.01.2013    source източник
comment
Искате ли чист изход след някакво състояние? какво ще кажете за извикването на Application.Terminate?   -  person jachguate    schedule 20.01.2013
comment
@jachguate Да, искам чист изход. Но това извикване все още води до показване на формуляра в случаите, когато показването на формуляра не би било логично.   -  person Glenn1234    schedule 20.01.2013
comment
Къде във въпроса си заявявате, че не искате да се показва никакъв формуляр?   -  person jachguate    schedule 20.01.2013
comment
@jachguate последния коментар на примерния код. Показването на формуляра, ако имам това в (например), събитието Create, би означавало, че е изпълнен още код.   -  person Glenn1234    schedule 20.01.2013
comment
Не е едно и също нещо. Ако искате да предотвратите изпълнението на повече код, повдигнете изключение. Ако не се справите с изключението сами, то ще достигне до манипулатора на изключения в главния цикъл. Application.Terminate; + Прекратяване; ще инструктира приложението да се изключи (чист изход) и ще предотврати изпълнението на повече код.   -  person jachguate    schedule 20.01.2013
comment
Но изключенията не работят така. Поставям повикване за повишаване там и то прави изключението и извежда кутия за съобщения, но след това формулярът се появява нормално, където мога да правя каквото си искам във формуляра.   -  person Glenn1234    schedule 20.01.2013
comment
Защо променихте кода във въпроса? както го показвате сега, първото вдигане предотвратява приложението. Прекратете да бъде извикан.   -  person jachguate    schedule 20.01.2013
comment
Защото исках да сложа това, което всъщност опитвах тук, вместо някакъв произволен примерен код.   -  person Glenn1234    schedule 20.01.2013


Отговори (4)


За да извършите необичайно прекъсване, извикайте Halt(), предавайки изходния код.

if CatastropicErrorDetected then
begin
  ... show error message
  Halt(1);
end;

В Windows това води до извикване на TerminateProcess и ще спре изпълнението там и тогава .

Забелязвате, че не се извършва почистване и обикновено това е, което искате. Тъй като извършвате тази проверка при стартиране на приложението, не трябва да има нищо за почистване.

person David Heffernan    schedule 20.01.2013

IMHO единственият чист начин би бил проверката за „Фатални условия“, преди приложението да се стартира.

program Project2;

uses
  Forms,Dialogs,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  ReportMemoryLeaksOnShutDown := true;
  Application.Initialize;
  if True then // your condition here
    begin
      MessageDLG('Fatal Error',mtError,[mbok],0);
    end
  else
    begin
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end;
end.

Всеки друг подход ще има странични ефекти

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
    FSL:TStringList;
  public
   Destructor Destroy;override;
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

destructor TForm1.Destroy;
begin
  FreeAndNil(FSL);
  Showmessage('Will never be seen with Application.Terminate + HALT');
  inherited;
end;

procedure TForm1.FormCreate(Sender: TObject);
const
  Testing=0; // try 1 and 2 too
begin

   FSL:=TStringList.Create;
   Try
      raise Exception.Create('Terminating now');
   except
     case Testing of
         0: begin
             // exception object will not be freed other code will be prevented, Form won't be shown
             Application.Terminate;
             HALT;
            end;
         1: begin
             // exception object will not be freed Form won't be shown
             HALT;
            end;
         2: begin
             // clean but Form will be shown
             Application.Terminate;
            end;

     end;
   end;

end;

end.
person bummi    schedule 20.01.2013
comment
Вашият първи подход, който се опитах да предложа, но OP изглежда се тревожи за модифициране на *.dpr файлове. - person TLama; 20.01.2013
comment
@TLama ти изтри поста си? Може би трябва да го възстановите, аз бих изтрил моя, ако възстановите вашия. Според мен, ако е възможно да се избегнат твърди прекъсвания, това трябва да се направи. - person bummi; 20.01.2013
comment
Нека го запазим както е. Вашият отговор предоставя по-широк обхват от моя. - person TLama; 20.01.2013
comment
@bummi Научихме, че твърдите прекъсвания са лоши в дните на DOS/Win3.1. Но с NT базиран Windows, *nix и т.н. няма проблем. ОС си връща всички ресурси. Прекратяването на нишки във все още работещ процес е лошо. Извикването на ExitProcess е добре. - person David Heffernan; 20.01.2013
comment
@DavidHeffernan Знам и съм съгласен, доколкото ОС е засегната. Но все още съм на мнение, че предотвратяването, ако е възможно, е по-добрият начин, доколкото може да се получи друг ефект. Оставяне на tmp файлове, окачване на BDE и подобни неща. - person bummi; 20.01.2013
comment
Временни файлове могат да се създават с FILE_FLAG_DELETE_ON_CLOSE. Ако BDE не може да се справи с прекратяването на процеса, тогава BDE е гадно. Ако сте заседнали с нещо подобно, тогава разбира се, съгласен съм, че става по-сложно. - person David Heffernan; 20.01.2013

Можете да инструктирате глобалния обект на приложението да прекрати програмата, като извикате Application.Terminate< /a>.

Извикайте Terminate, за да прекратите приложението програмно. Като извикате Terminate, вместо да освободите обекта на приложението, позволявате на приложението да се изключи по организиран начин.

Terminate извиква функцията PostQuitMessage на Windows API за извършване на правилно изключване на приложението. Прекратяването не е незабавно.

Тъй като извикването може да се случи по-дълбоко в стека, можете също да повдигнете изключение, и вие кодирате програмата си така, че да не я обработва, за да позволите на изпълнението да достигне до главния цикъл на приложението и манипулаторът на изключение по подразбиране да го улови.

По този начин вие ефективно предотвратявате изпълнението на повече код във вашето приложение.

В кода може да изглежда така:

if not FileExists(FilePath1) then
  begin
    MessageDlg(FilePath1 + ' does not exist and is required for this program to function.',
               mtWarning, [mbOK], 0);

    Application.Terminate;
    Abort;  //raising a EAbort just as an example
  end;

В зависимост от това къде се извиква този код, съветвам ви да не показвате съобщението директно, а по-скоро да предизвикате изключение със съобщението и да оставите метода HandleException по подразбиране на обекта на приложението да покаже съобщението вместо вас:

if not FileExists(FilePath1) then
  begin
    Application.Terminate;
    raise EMyFatalException.Create(FilePath1 
      + ' does not exist and is required for this program to function.');
  end;

Което ми изглежда по-естествено. EMyFatalException е хипотетичен клас изключение, който можете да декларирате и никога да не обработвате във вашите клаузи за изключение.

person jachguate    schedule 20.01.2013
comment
Вторият кодов блок просто показва бързо кутия и след това прекратява. Тук имам Delphi 3, върху който го тествам в момента (всичко, което ми е удобно в момента), ако е полезно да знам. - person Glenn1234; 20.01.2013
comment
Показва съобщението, след това мига главния формуляр и прекратява - общо взето най-доброто, което мога да направя сега. - person Glenn1234; 20.01.2013
comment
В D3 мисля, че е... тъй като не искате да докосвате DPR. Можете да опитате да зададете свойството Visible на главната форма на false, но съм почти сигурен, че няма да работи. Чувствайте се свободни да опитате и да ни уведомите. - person jachguate; 20.01.2013
comment
Докосвам DPR многократно и нямам проблем с поставянето на отметката в този пример в DPR (стига IDE да ми позволява, това е част от причината, поради която не искам да влизам в навика) . Проблемът, който имам, също така включва проверки, които са в рамките на самия код за изпълнение (т.е. ако услугата не е налична), която продължава след изключения, така че извършването на правилното прекратяване е проблем и там. Задаването на TForm.Visible на false даде същия резултат. - person Glenn1234; 20.01.2013
comment
Съвсем нормално е да модифицирате DPR, дори в D3. Версия с грешки може да го обърка, ако добавите нов формуляр за автоматично създаване, но стига да знаете какво правите, можете да го накарате да работи отново, ако е необходимо. Ако имате твърде много проверки за извършване, можете да групирате всички в нов модул и да добавите едно извикване на процедура във вашия DPR. Можете да направите това дори булева функция и да използвате подход, подобен на отговора на TLama, за да пропуснете стартирането на програмата, ако нещо се оправи грешно. - person jachguate; 20.01.2013
comment
Ако това наистина е катастрофална грешка, може би трябва да използвате Halt или дори ExitProcess() вместо Application.Terminate. Application.Terminate не излиза веднага, както знаете... - person iMan Biglari; 20.01.2013

Можете да напишете свой собствен Application.OnException манипулатор, например:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure HandleException(Sender: TObject; E: Exception);
  end;

var
  Form1: TForm1;

type
  EMyFatalError = class(Exception);

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Application.OnException:= HandleException;
  raise EMyFatalError.Create('OOPS!');
end;

procedure TForm1.HandleException(Sender: TObject; E: Exception);
begin
  Application.ShowException(E);
  if E is EMyFatalError then
    Application.Terminate;
end;

end.
person kludg    schedule 20.01.2013