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.
Datum: 2011-04-10 22:49:00 Tagy: Delphi 2009, RTL, začátečníci, ladeni