Něco o exceptions

vložil Radek Červinka 10. dubna 2011 23:49

Koncept výjimek v Delphi je dobře vymyšlen a dokonce jsem kdesi četl, že je částečně patentován, jelikož v době Delphi 1 byl naprosto převratný způsobem zpracování za běhu (nějak to souviselo s efektivním odvíjením zásobníku při výjimce, detaily si nepamatuji a snad se nepletu).

Mimochodem Windows podporují výjimky až od 32bit verzí, tj. Delphi 1, které bylo 16bit mělo výjimky vlastní a až Delphi 2 mapují část výjimek na výjimky Windows.

Od Delphi 1 je základem třída Exception, která byla v Delphi 2009 trochu rozšířena - přece jen je to už pár let.

Pro začátečníky je stručné pojednání o výjimkách v článku pro začátečníky (pravý sloupec), pro ty ostatní: třída byla rozšířena o objekt InnerException a podporu pro StackTrace. Dám sem rovnou ukázku kódu, na formuláři jsou dvě tlačítka a jejich obsluhy.

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    btn1: TButton;
    btn2: TButton;
    procedure btn1Click(Sender: TObject);
    procedure btn2Click(Sender: TObject);
  private
    { Private declarations }
    procedure mTest;
    procedure mTest2;
  public
    { Public declarations }
    e:Exception;
  end;

var
  Form1: TForm1;

implementation
uses
 JclDebug; //jen pro výpis stacku
{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
begin
  try
    mTest;
  except
    on E:Exception do
      ShowMessage(E.ToString);
  end;
end;

procedure TForm1.btn2Click(Sender: TObject);
begin
  try
    mTest2;
  except
    on E:Exception do
    begin
      ShowMessage(E.ToString);
      if E.InnerException <> nil then
        ShowMessage(E.InnerException.Message);
      ShowMessage(E.StackTrace)
    end;
  end;
end;

procedure TForm1.mTest;
var
  sl:TStringList;
begin
  try
    sl := TStringList.Create;
    try
      raise SysUtils.EProgrammerNotFound.Create('Tady to někdo neudělal dobře.');

    finally
      sl.Free;
    end;
  except
    on E:Exception do
    begin
      E.Message := E.Message + #13#10 + 'Další řádek';
      raise;
    end;
  end;
end;

type
  EOwnException =class(Exception);

procedure TForm1.mTest2;
var
  sl:TStringList;
begin
  try
    sl := TStringList.Create;
    try
      raise SysUtils.EProgrammerNotFound.Create('Tady to někdo neudělal dobře.');

    finally
      sl.Free;
    end;
  except
    on E:Exception do
    begin
      Exception.RaiseOuterException(EOwnException.Create('Nova exception'));
    end;
  end;
end;

// podpora pro výpis stacktrace via JCL

//http://www.sql.ru/forum/actualthread.aspx?tid=641304&hl=stacktrace
function GetExceptionStackInfoProc(P: PExceptionRecord):Pointer;
begin
  Result := TJclStackInfoList.Create(False, 0, nil);
end;

function GetStackInfoStringProc(Info: Pointer):String;
var
  Stack:TJclStackInfoList;
  Str:TStringList;
begin
  if Info = nil then Exit;
  str := nil;
 try
  str := TStringList.Create;
  Stack := TJclStackInfoList(Info);
  Stack.AddToStrings(Str);
  Result :=  Str.Text;
 finally
    FreeAndNil(Str);
 end;

end;

procedure CleanUpStackInfoProc(Info: Pointer);
begin
  FreeAndNil(TJclStackInfoList(Info));
end;

initialization
  Exception.CleanUpStackInfoProc := CleanUpStackInfoProc;
  Exception.GetExceptionStackInfoProc := GetExceptionStackInfoProc;
  Exception.GetStackInfoStringProc :=   GetStackInfoStringProc;
end.

mTest a mTest2 jsou dvě ukázky jak při odchycení výjimky modifikovat výjimku. mTest bude fungovat i v Delphi před 2009, druhý způsob používá InnerException, kdy

Exception.RaiseOuterException(EOwnException.Create('Nova exception'));

vyvolá novou výjimku a právě zpracovávaná je přesunutá do InnerException. To samozřejmě lze opakovat a tak jde přes InnerException vybudovat řetěz výjimek (normálně by byla jen dostupná historie volání, ale všechny další informace pak musí být součástí jedné výjimky podobně jako v předchozím případě).

Metoda ToString pak samozřejmě prochází všechny dostupné InnerException.

Druhá nová věc je možnost výpisu zásobníku. Exception nyní obsahuje property StackTrace, která volá rutinu pro získání zásobníku, která se nastavuje přes

 Exception = class(TObject)
….
  class var
    GetExceptionStackInfoProc: function (P: PExceptionRecord): Pointer;
    GetStackInfoStringProc: function (Info: Pointer): string;
    CleanUpStackInfoProc: procedure (Info: Pointer);
    class procedure RaiseOuterException(E: Exception); static;
 end; 

viz sekce initialization v příkladu.

Uvedené zjišťování používá klasické trasování volání bez použití InnerException z JCL.

Tagy: , , ,

Novinky v Delphi

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců