Manejador de excepciones avanzadas con Delphi
El otro día comentaba con uno de los compañeros de trabajo que sería interesante poder disponer de la información de la pila (Call Stack) en tiempo de ejecución, y poder visualizar la información del error con más información, es decir con la información de la pila para saber de dónde procedía el error y así seguirlo con facilidad. Hay que ser pragmáticos con éste tema y evitar hacer logs inmensos de seguimiento simplemente porqué no encontramos el error. De ésta manera podríamos encontrar el error rápidamente, analizando la Call Stack. Si os fijáis en la Call Stack de delphi, cuando ejecutamos una aplicación, y ponemos un punto de interrupción, podemos ver por dónde va pasando el programa y los métodos que va ejecutando para llegar hasta nuestro punto de interrupción. En la siguiente imágen podéis ver una ejecución simple de una aplicación y su paro en un break point.
Para mi ejemplo haré reposting, y utilizaré el ejemplo que escribió Sergey Shirokov y Alex P en Advanced Exception Handler. En su artículo comentan concretamente lo que yo quiero obtener de la pila. Para la obtención de los datos avanzados, necesitaré el siguiente componente: ExWatcher de la mano de Tomek Guz (Solo la versión para Delphi 5). Además utilizaremos el proyecto demo, llamado ExWatcherDemo.
Ahora compilamos, hacemos build e Install. Una vez instalado el componente nos aparecerá el siguiente mensaje:
Ahora una vez instalado el componente, tenemos que añadir la ruta dónde están los .dcu en la library path de delphi. Ésto se hace iéndo a -> Tools -> Options y en el nodo Environment Options -> Delphi Options -> Library -Win32, añadimos la ruta de la carpeta del ExWatcher en Library Path.
He modificado el TMemo y lo he hecho un poco más grande y he modificado también los colores. Ahora una vez tenemos el proyecto OK, tenemos que modificar los siguientes parámetros para su compilación:
Marcamos las opciones de compilación : Stack Frames y de Linkado : Map File -> Publics. De ésta manera creamos los ficheros necesarios que nos darán la información de los valores de la pila en tiempo de ejecución.
Una vez descargada la aplicación, la copiamos dentro de nuestra aplicación demo, y ejecutamos lo siguiente desde el intérprete de comandos:
map2dbg.exe exwatcherdemo.exe
y nos aparecerá el siguiente resultado:
converted 4150 symbols
Ahora, si ejecutamos el ejemplo y lanzamos alguna excepción obtendremos todas las llamadas de la Call Stack en nuestro TMemo:
Como podéis ver, no tiene desperdicio. Ahora ya podemos crear aplicaciones con mayor control sobre los errores. Espero que os haya servido de ayuda!.
Aquí os dejo con el proyecto de prueba que he utilizado: ExWatcherDemo Project.rar. Y el código fuente de la unidad principal para que veáis lo fácil que es utilizar el componente:
Madshi.net.
EurekaLog.
Para mi ejemplo haré reposting, y utilizaré el ejemplo que escribió Sergey Shirokov y Alex P en Advanced Exception Handler. En su artículo comentan concretamente lo que yo quiero obtener de la pila. Para la obtención de los datos avanzados, necesitaré el siguiente componente: ExWatcher de la mano de Tomek Guz (Solo la versión para Delphi 5). Además utilizaremos el proyecto demo, llamado ExWatcherDemo.
- Instalando el componente ExWatcher
Ahora compilamos, hacemos build e Install. Una vez instalado el componente nos aparecerá el siguiente mensaje:
Ahora una vez instalado el componente, tenemos que añadir la ruta dónde están los .dcu en la library path de delphi. Ésto se hace iéndo a -> Tools -> Options y en el nodo Environment Options -> Delphi Options -> Library -Win32, añadimos la ruta de la carpeta del ExWatcher en Library Path.
- Ejecutando la demo
He modificado el TMemo y lo he hecho un poco más grande y he modificado también los colores. Ahora una vez tenemos el proyecto OK, tenemos que modificar los siguientes parámetros para su compilación:
Marcamos las opciones de compilación : Stack Frames y de Linkado : Map File -> Publics. De ésta manera creamos los ficheros necesarios que nos darán la información de los valores de la pila en tiempo de ejecución.
- map2dbg
Una vez descargada la aplicación, la copiamos dentro de nuestra aplicación demo, y ejecutamos lo siguiente desde el intérprete de comandos:
map2dbg.exe exwatcherdemo.exe
y nos aparecerá el siguiente resultado:
converted 4150 symbols
Ahora, si ejecutamos el ejemplo y lanzamos alguna excepción obtendremos todas las llamadas de la Call Stack en nuestro TMemo:
Como podéis ver, no tiene desperdicio. Ahora ya podemos crear aplicaciones con mayor control sobre los errores. Espero que os haya servido de ayuda!.
Aquí os dejo con el proyecto de prueba que he utilizado: ExWatcherDemo Project.rar. Y el código fuente de la unidad principal para que veáis lo fácil que es utilizar el componente:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, clExWatcher, ShowExcept;
type
TMainForm = class(TForm)
btnDelphiException: TButton;
btnDelphiSafeCallException: TButton;
btnSystemException: TButton;
clExceptWatcher: TclExceptWatcher;
btnSystemSafeCallException: TButton;
btnTryExcept: TButton;
memStackLog: TMemo;
procedure clExceptWatcherDelphiException(Sender: TObject; E: Exception;
EI: TclExceptionInformation; StackTracer: TclStackTracer);
procedure clExceptWatcherDelphiSafeCallException(Sender,
Target: TObject; E: Exception; EI: TclExceptionInformation;
StackTracer: TclStackTracer);
procedure clExceptWatcherSystemException(Sender: TObject;
EI: TclExceptionInformation; StackTracer: TclStackTracer);
procedure clExceptWatcherSystemSafeCallException(Sender,
Target: TObject; EI: TclExceptionInformation;
var NotifyTarget: Boolean; StackTracer: TclStackTracer);
procedure btnDelphiExceptionClick(Sender: TObject);
procedure btnDelphiSafeCallExceptionClick(Sender: TObject);
procedure btnSystemExceptionClick(Sender: TObject);
procedure btnSystemSafeCallExceptionClick(Sender: TObject);
procedure btnTryExceptClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
procedure SimulateDelphiException();
procedure SimulateSystemException();
procedure SimulateDelphiSafeCallException(); safecall;
procedure SimulateSystemSafeCallException(); safecall;
procedure ShowStack(Frames: TclStackFrames);
procedure ExceptionHandler(Sender: TObject; E: Exception);
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.clExceptWatcherDelphiException(Sender: TObject;
E: Exception; EI: TclExceptionInformation; StackTracer: TclStackTracer);
begin
memStackLog.Clear();
memStackLog.Lines.Add('Delphi Exception:');
ShowStack(StackTracer.GetCallStack(EI));
end;
procedure TMainForm.clExceptWatcherSystemException(Sender: TObject;
EI: TclExceptionInformation; StackTracer: TclStackTracer);
begin
memStackLog.Clear();
memStackLog.Lines.Add('System Exception:');
ShowStack(StackTracer.GetCallStack(EI));
end;
procedure TMainForm.clExceptWatcherDelphiSafeCallException(Sender,
Target: TObject; E: Exception; EI: TclExceptionInformation;
StackTracer: TclStackTracer);
begin
memStackLog.Clear();
memStackLog.Lines.Add('Delphi SafeCall Exception:');
ShowStack(StackTracer.GetCallStack(EI));
end;
procedure TMainForm.clExceptWatcherSystemSafeCallException(Sender,
Target: TObject; EI: TclExceptionInformation; var NotifyTarget: Boolean;
StackTracer: TclStackTracer);
begin
memStackLog.Clear();
memStackLog.Lines.Add('System SafeCall Exception:');
ShowStack(StackTracer.GetCallStack(EI));
end;
procedure TMainForm.btnDelphiExceptionClick(Sender: TObject);
begin
SimulateDelphiException();
end;
procedure TMainForm.btnSystemExceptionClick(Sender: TObject);
begin
SimulateSystemException();
end;
procedure TMainForm.btnDelphiSafeCallExceptionClick(Sender: TObject);
begin
SimulateDelphiSafeCallException();
end;
procedure TMainForm.btnSystemSafeCallExceptionClick(Sender: TObject);
begin
SimulateSystemSafeCallException();
end;
procedure TMainForm.btnTryExceptClick(Sender: TObject);
begin
try
try
SimulateSystemException();
except
raise;
end;
except
end;
end;
procedure TMainForm.SimulateDelphiException;
begin
raise Exception.Create('Test Delphi exception');
end;
procedure TMainForm.SimulateSystemException;
var
p: PChar;
begin
p := PChar(5);
p^ := #9; //Access Violation here
end;
procedure TMainForm.SimulateDelphiSafeCallException;
begin
raise Exception.Create('Test Delphi SafeCall exception');
end;
procedure TMainForm.SimulateSystemSafeCallException;
var
p: PChar;
begin
p := PChar(5);
p^ := #9; //Access Violation here
end;
procedure TMainForm.ShowStack(Frames: TclStackFrames);
var
i: Integer;
begin
memStackLog.Lines.BeginUpdate();
for i := 0 to Frames.Count - 1 do
begin
memStackLog.Lines.Add(Format('%x: %s - %s', [Integer(Frames[i].CallAddress),
ExtractFileName(Frames[i].ModuleName), Frames[i].FunctionName]));
end;
memStackLog.Lines.EndUpdate();
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Application.OnException := ExceptionHandler;
end;
procedure TMainForm.ExceptionHandler(Sender: TObject; E: Exception);
begin
ShowExceptionForm(E.Message);
end;
end.
- Enlaces de interés:
Madshi.net.
EurekaLog.
This comment has been removed by the author.
ReplyDeleteHi there Jordi! It seems to be an absolutely great component. But, woah! @#$**)!, it doesn't work for me on Vista x64! It just appears the last line, none of the call stack. At first I thought it was my fault by installation problems. Then, I had a try putting the same .exe on a winXP x32 machine, and it perfectly worked! Vista seems to be having fun with us... Can anyone have a try on Vista x32?
ReplyDeleteBTW, thank you for the post Jordi!
Hi!, that's the problem with de .dbg information, it only works with 32 bits. Don't worry about that, I'll take a look. Thank you for your comment!
ReplyDeleteYou can find more information from here:
ReplyDeletewww.madshi.net/enumStuff.zip