Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
497 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi



Loremo - the 1.5 liter car coming in 2009




Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (5)


Exception Handling in Delphi - Framework to log the complete traverse informationFormat this article printer-friendly!Bookmark function is only available for registered users!
Product:
Delphi all versions
Category:
Object Pascal
Skill Level:
Scoring:
Last Update:
04/20/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet Exceptions Exceptions-Call-Stack Exception-Framework
Times Scored:
9
Visits:
5475
Uploader: Sriram Ambuga
Company: Symmetry Infotech
Reference: N/A
 
Question/Problem/Abstract:
One of the hiccups in huge applications developed in Delphi is tracing exceptions till the root. Delphi as such provides a lovely mechanism to trap exceptions by way of try..except blocks. But then, in order to utilize this mechanism we need to have a proper framework. In this write up I am explaining the framework designed by me for handling exceptions. In this mechanism we get the complete information like the method names, unit names and the traverse information of a particular exception. The only limitation being the line number for which we might need to provide the Debug information or Map files. This frame work is designed to work under all the compiling/linking conditions and so does not include the mechanism to provide the line numbers.

Also, expecting opinions and responses to make it much better.

Answer:



To have a proper control of the exceptions in an Application, a new class is created - ECustomException class with Exception as its parent class. An array of Pointer is introduced in this class basically to store the error details that are thrown within the application. This class has two constructors, the need for both will be known when we implementation part. A record has been introduced which can be changed by the implementor based on his requirements. Currently the record stores the Unit Name, Method and Module name.

Before we get down to the actual code the let me explain the guidelines kept in mind when designing the framework:
a) A complete traverse information of the exception.
b) A mechanism to display a meaningul information to the end user, but a detailed technical information to the developer.
c) Centralized Exception displaying and logging mechanism (Application.OnException method).

{******************************************************************************}
{                                                                              }
{       Custom Exception class                                                 }
{       Author: Sri Ram Ambuga Nandakumar                                      }
{                                                                              }
{ Disclaimer                                                                   }
{ ----------                                                                   }
{                                                                              }
{ THE FILES ARE PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND WHETHER    }
{ EXPRESSED OR IMPLIED.                                                        }
{                                                                              }
{ In no event shall the author be held liable for any damages whatsoever,      }
{ including without limitation, damages for loss of business profits,          }
{ business interruption, loss of business information, or any other loss       }
{ arising from the use or inability to use the unit.                           }
{******************************************************************************}
unit UExceptionObj;

interface

uses
  SysUtils;

type
  PErrorDetails = ^TErrorDetails;                  //----------------------------------
  TErrorDetails = record                           //                                 |   Error Detail record.
    sUnit: string;                                 //                                 |-> Can be changed by the implementor
    sModule: string;                               //                                 |   To suit his requirement
    sMethod: string;                               //                                 |
  end;                                             //----------------------------------
  TCallStack = array of PErrorDetails;                                                 // <- Array of Pointer

type
  ECustomException = class(Exception)
  private
    FCallStack: TCallStack;
    function GetStackLength: integer;
    function GetErrorDetails(Index: integer): TErrorDetails;
  protected
  public
    constructor Create(AMessage: string; AErrorDetails: TErrorDetails); overload;
    constructor Create(AMessage: string; AException: ECustomException); overload;
    destructor Destroy; override;

    procedure AddToCallStack(AErrorDetails: TErrorDetails);
    property StackLength: integer read GetStackLength;
    property ErrorDetails[Index: integer]: TErrorDetails read GetErrorDetails; default;
  end;

implementation

{ ECustomException }

constructor ECustomException.Create(AMessage: string;
  AErrorDetails: TErrorDetails);
begin
  inherited Create(AMessage);
  AddToCallStack(AErrorDetails);
end;

procedure ECustomException.AddToCallStack(AErrorDetails: TErrorDetails);
var
  ptrErrDet: PErrorDetails;
begin
  New(ptrErrDet);
  ptrErrDet^ := AErrorDetails;
  SetLength(FCallStack, High(FCallStack) + 2);
  FCallStack[High(FCallStack)] := ptrErrDet;
end;

constructor ECustomException.Create(AMessage: string;
  AException: ECustomException);
var
  iCount: integer;
begin
  inherited Create(AMessage);

  for iCount := Low(AException.FCallStack) to High(AException.FCallStack) do
    AddToCallStack(AException.FCallStack[iCount]^);
end;

destructor ECustomException.Destroy;
var
  iCount: integer;
begin
  inherited;
  for iCount := Low(FCallStack) to High(FCallStack) do
    Dispose(FCallStack[iCount]);

  if Assigned(FCallStack) then
    Finalize(FCallStack);
end;

function ECustomException.GetStackLength: integer;
begin
  Result := High(FCallStack);
end;

function ECustomException.GetErrorDetails(Index: integer): TErrorDetails;
begin
  if (Index > -1) and (Index < StackLength) then
    Result := FCallStack[Index]^;
end;

end.
//****************************************************************************************************************

As we can see the Exception class takes care of storing the error information that are given to it. Now coming to the interface function which should be called from all the procedures in the Application.

//****************************************************************************************************************
procedure GlobalExceptionHandler(AException: Exception; sUnit, sModule,
  sProcedure: string);
var
  recErrDet: TErrorDetails;
begin
  recErrDet.sModule := sModule;
  recErrDet.sProcedure := sProcedure;
  recErrDet.sUnit := sUnit;

  if AException is ECustomException then        
  begin
    { Here we can see if it is ECustomException then it adds the entry in the Exception object and then raises the same error.
      Note: since raise creates a new object the earlier exception's stack info has to be passed on to the newly created one.

      This is the purpose of one of the Constructor.
    }
    (AException as ECustomException).AddToCallStack(recErrDet);
    raise ECustomException.Create(AException.Message, (AException as ECustomException));
  end
  else
    { If it is any other Error then create our exception class with the earlier exception's message. This happens only at the source of the              
      error only that is for the first time. Additional code can be written here to change the message of certain Exceptions like
        if EAccessViolation then raise ECustomException.Create('An error occurred while accessing a resource', recErrDet); ;) }
    raise ECustomException.Create(AException.Message, recErrDet);
end;
//****************************************************************************************************************

Additionally, this global exception handler can be fine tuned as per the requirement of the application.

In the Application.OnException method just a loop will give all the information about the exception.

Now all the methods in my application would look like below:

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    CallAProcedure
  except
    on E: Exception do
      GlobalExceptionHandler(E, 'UnitName', Application.ExeName, 'Button1Click');
  end;
end;


procedure CallAProcedure;
begin
  try
    CallRaiseAnException;
  except
    on E: Exception do
      GlobalExceptionHandler(E, 'UnitName', Application.ExeName, 'CallAProcedure');
  end;
end;

procedure CallRaiseAnException;
begin
  try
    RaiseAnException;
  except
    on E: Exception do
      GlobalExceptionHandler(E, 'UnitName', Application.ExeName,
        'CallRaiseAnException');
  end;
end;

procedure RaiseAnException;
begin
  try
    raise Exception.Create('This is the original error message');
  except
    on E: Exception do
      GlobalExceptionHandler(E, 'UnitName', Application.ExeName,
        'CallRaiseAnException');
  end;
end;

Now in the above case when the exception object reaches the Application's
OnException method, the information of procedures that it will contain will be:

  Button1Click
  CallAProcedure
  CallRaiseAnException
  RaiseAnException.


Please let me know your opinions and views regarding the same

Sri Ram Ambuga Nandakumar





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Missing item on Call Stack
    Derek Gibb (May 4 2006 5:01PM)

Hi Sriram

I have implemented the code from your article "Exception Handling in Delphi - Framework to log the complete traverse information", but for some reason when I run it (using your example) I don't get the Button1Click (in other words the initial method called to start the process) when I loop through the CallStack information.  Here is th code that I am using:

function GetCallStackDetails(AException : Exception) : string;
var
   recErrDet : TErrorDetails;
   iCount : integer;

begin
   Result := '';
   for iCount := 0 to (AException as ECustomException).StackLength-1 do
   begin
      recErrDet := (AException as ECustomException).ErrorDetails[iCount];
      Result := Result + recErrDet.sProcedure   end;
end;
Respond

RE: Missing item on Call Stack
Derek Gibb (May 5 2006 2:58PM)

Hi Sriram

I have found what the problem was.  You need to change the GetStackLength to the following:

function ECustomException.GetStackLength: integer;
begin
  Result := High(FCallStack)+1;
end;

Note the "+1".  I am not sure why this is the case, but it appears to work now so I am not complaining.

Cheers

Derek
Respond

RE: RE: Missing item on Call Stack
Sriram Ambuga (May 5 2006 3:38PM)

Hello Derek,

Yes there is a problem in the code that is posted.

In  the 'GetErrorDetails' method it should be (Index <= StackLength) instead of Index < StackLength.

HTH.
Respond

RE: Missing item on Call Stack
Sriram Ambuga (May 5 2006 3:43PM)

Your solution works too.
Respond

RE: RE: Missing item on Call Stack
Derek Gibb (May 5 2006 6:25PM)

Thanks Sriram!

It is working perfectly now.  I will try your solution when I get to work on Monday!

Cheers

Derek
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
R. Lefter
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)