community.borland.com

Article #25665: Delphi Error Handling Techniques



Some information on setting up error handling in Delphi  program code.

Solution:

Delphi Error Handling Techniques
================================
   The best way to catch exceptions is by defensive programming.

   For example, all code sections which allow for input from a user
   should be wrapped in a "try" block.

   This piece of code takes the form :

   try
     
   except

     on  
         do begin
            
         end;
     end;

   finally

     do begin
       .
     end;

   Execution begins in the try section. 

   If no exceptions are raised, the except section is skipped 
   and the finally block (if one exists) is executed.

   If an exception is raised, code execution immediately goes
   to the exception section.  The except section is searched.

   If there is an "on" block, an attempt will be made to look
   for a matching exception condition.  If a matching exception
   type is found, the code in that exception block is executed.

   If no matching exception type is found, the default behavior
   of the class will be executed (usually displaying an error
   message).

   If no "on" block exists, the code in the except section will
   be executed for all error conditions.

   After any exception code is executed, the program will continue
   on to the "finally" block, or if no finally block is provided,
   execution will continue on the next statement after the
   exception block. 

   An example (provided from the classes.pas file in Delphi 4 source)
   is shown below.  This example shows the general layout and
   the use of a finally clause. Note: the "exception" clause is not
   used.

     function GetClass(const AClassName: string): TPersistentClass;
     var
       I: Integer;
     begin
       with ClassList.LockList do
       try     // ClassAliasList protected by ClassList lock
         for I := 0 to Count - 1 do
         begin
           Result := Items[I];
           if Result.ClassNameIs(AClassName) then Exit;
         end;
         I := ClassAliasList.IndexOf(AClassName);
         if I >= 0 then
         begin
           Result := TPersistentClass(ClassAliasList.Objects[I]);
           Exit;
         end;
         Result := nil;
       finally
         ClassList.UnlockList;
       end;
     end;

   This function (GetClass), attempts to select a class from a
   list of classes from the classes.pas file.

   The ClassList is first locked.

   Then a search is done for the class name or an alias.

   If the class is found, the class name is assigned to the
   parameter "Result" then the function exits.

   If the class name is not found, the value "nil" is assigned
   to the parameter "Result".

   In either case, the finally clause is executed which unlocks
   the ClassList.

   Another example (from "Learning Delphi 4 in 21 Days - pg 583)
   shows some exception handling.

     if OpenPictureDialog.Execute then begin
        Child := TChild.Create(Self);
        with Child.Image.Picture do begin

        try
           LoadFromFile(OpenPictureDialog.Filename);
           Child.ClientWidth := Width;
           Child.ClientHeight := Height;
        except
           Child.Close;
           Application.MessageBox(
             `This file is not a Windows image file.',
             `Picture Viewer Error', MB_ICONHAND or MB_OK);
           Exit;
        end;
     end;
     Child.Caption :=
       ExtractFileName(OpenPictureDialog.FileName);
     Child.Show;
   end;

   In this case, the user has supplied a file name which should
   contain a picture file to display.  If there is an error
   in attempting to load the file (wrong file type in this case),
   the Child.Close routine error exception will be raised.

   The exception code displays a message and exits
   from the display routine.

   The finally clause, is not used in this example.

   If this error does not occur (Child.close), the code after the
   exception handler gets executed (Child.Caption :=  .  .  .).

   These examples display one requirement, and one weakness to error
   handling code.

   The requirement is that you should know what exceptions can be 
   raised by a routine before you use it (to make sure that you can 
   perform the proper recovery if the exceptions occur).

   If you do not trap a specific exception, the code following the 
   try block will attempt to execute.

   Routines may have several specific error conditions trapped and
   a few general error conditions.  It is good programming practice
   to trap the specific errors first, and then after, trap the
   more general ones.

   Error routines can either be found in the documentation for a
   component, or in the source code.  Delphi 4 VCL source code, for
   example is, by default, located at

      "Program FilesBorlandDelphi 4SourceVCL"

   Look for the key word "raise" which is used to raise an exception. 


   An example procedure from classes.pas (Delphi 4 VCL source) with
   the use of the "raise" exception is shown below.  In this case,
   the name of the raised exception is "EFilerError".

     procedure RegisterClass(AClass: TPersistentClass);
     var
       AClassName: string;
     begin
       with ClassList.LockList do
       try
         while IndexOf(AClass) = -1 do
         begin
           AClassName := AClass.ClassName;
           if GetClass(AClassName) <> nil then
             raise EFilerError.CreateFmt(SDuplicateClass, [AClassName]);
           Add(AClass);
           if AClass = TPersistent then Break;
           AClass := TPersistentClass(AClass.ClassParent);
         end;
       finally
         ClassList.UnlockList;
       end;
     end;


Last Modified: 23-OCT-00