Last Updated:

Exception as a class | Delphi | Tutorial

Anyone who has written more or less complex programs is intuitively clear what exception handling (IC) is. Any interaction with the operating system for obtaining resources – disk space, memory, opening a file – may fail. Any calculation can result in a division by zero or an overflow. An additional factor in the occurrence of exceptions is contained in the data that programs can access. This is especially true in database applications.

Payment for the reliable operation of the program in such conditions is the introduction of numerous checks that can prevent incorrect actions in the event of an emergency situation. It is good if at the end of the next construct if.. then you can simply put an Exit statement. Usually, for the correct way out of the situation, you need to cancel a whole sequence of actions preceding the unsuccessful one. All this greatly confuses the program, masking the clear structure of the main algorithm.

When developing applications in Delphi, the programmer has the opportunity to use several mechanisms that provide exception handling. These are special operators of the Object Pascal language, and classes designed to program the response to errors.

Therefore, this chapter is devoted to... no, not how to write unmistakably; and how to protect applications from the effects of inevitable errors.


What is an exception? Intuitively, this is a kind of abnormal event that can affect the further implementation of the program. If you've written in Turbo Pascal or similar before, you've probably tried to avoid such situations by entering numerous data checks and feature return codes. You can get rid of this cumbersome code once and for all by adopting the mechanism implemented in Delphi.

The Delphi compiler generates code that intercepts any such abnormal event, saves the necessary data about the state of the program, and gives the developer... What can be output in an object-oriented programming language? Of course, the object. From object Pascal's perspective, an exception is an object.

You can obtain and process this object by providing a special language construct (try.. except). If such a design is not provided, the exception will still be handled - in the bowels of the VCL there are appropriate handlers surrounding all potentially dangerous places.

What is the difference between exceptional situations? How do you distinguish one exception from another? Because they are objects, they differ in class (object type). In the SYSUTILS module. PAS describes the Exception object type. It is the ancestor for all other objects – exceptional situations. Here he is:

Exception = class(TObject)
private
FMessage: string;
FHelpContext: Integer;
public
constructor Create(const Msg: string);
constructor CreateEmt(const Msg: string; const Args: array of const);
constructor CreateRes(Ident: Integer); overload;
constructor CreateRes(ResStringRec: PResStringRec); overload;
constructor CreateResFmt(Ident: Integer; const Args: array of const);
overload; constructor CreateResFmt(ResStringRec: PResStringRec; const Args: array of const);
overload;
constructor CreateHelp(const Msg: string; AHelpContext: Integer);
constructor CreateFmtHelp(const Msg: string; const Args:
array of const;
AHelpContext: Integer);
constructor CreateResHelp(Ident: Integer; AHelpContext: Integer);
overload;
constructor CreateResHelp(ResStringRec: PResStringRec; AHelpContext: Integer); overload;
constructor CreateResFmtHelp(ResStringRec: PResStringRec; const Args: array of const;
 AHelpContext: Integer); overload;
constructor CreateResFmtHelp(Ident: Integer; const Args: array of const; AHelpContext: Integer);
 overload;
property HelpContext: Integer read FHelpContext write FHelpContext;
property Message: string read FMessage write FMessage;
end;
ExceptClass = class of Exception;

As you can see from the above description of the Exception class, it has twelve (!) constructors that allow you to use text strings from application resources when creating an object (the name includes the Res string), text formatting (includes Fmt), and communication with the context of the help system (includes Help).

Constructors whose name contains the Fmt substring can insert parameter values into the generated text of the error message, as the standard Format function does:

If MemSize > Limit then
raise EOutOfMemory.CreateFmt('Cannot allocate more than %d
bytes',[Limit]);

If there is a substring Res in the title, it means that the message text will be loaded from the application's resources. This is especially useful when creating localized versions of software products, when you need to change the language of all messages without compiling anything again.

Finally, if a Help substring appears in the title, the constructor initializes the HelpContext property of the object being created. Naturally, a help system should be created and it should have an article related to this context. Now the user can request assistance for this situation, say, by pressing the F1 key at the time of displaying the MESSAGE about the IP.

The Exception type generates numerous child types that correspond to common cases of I/O errors, memory allocation, etc. The Delphi 7 exception tree is shown in Figure 7. 3.1.

Note that the Exception type and its descendants represent an exception to the rule that all object types should be named with the letter T.

Exception descendants begin with E, such as EZeroDivide.

Delphi 7 Professional Self-Study Guide › Handling Exceptions › Exception As a Class

Fig. 3.1. Delphi 7 Exception Object Tree

To save space, the descendants of several important objects are not shown. Below are the tables. 3.1-3.3 containing descriptions of these groups of exceptional situations.

You can initiate an exception yourself when performing certain actions. But, although the syntax of the constructor of the Exception object is similar to the constructors of all other objects, it is created in a special way.

Table 3.1. Memory exceptions (generated by EHeapException).

TypeOccurrence condition
EOutOfMemoryNot enough space in the heap (memory)
EOutOfResourcesLack of system resources
EInvalidPointerInvalid pointer (usually nil)

Table 3.2. Exceptional situations of integer mathematics (derived from EIntError).

TypeOccurrence condition
EDivByZeroTry divisible by zero (integer)
ERangeErrorA number or expression exceeds a valid range
EIntOverflowInteger overflow

Table 3.3. Floating-point mathematics exceptions (derived from EMathError).

TypeOccurrence condition
EInvalidOpIncorrect operation
EZeroDivideTrying to divide by zero
EOverflowFloating-point overflow
EUnderflowDisappearance of order
EInvalidArgumentInvalid argument of mathematical functions

To do this, use the raise statement, followed by an instance of an object of type Exception as a parameter. Usually, the operator is immediately followed by the constructor of the IC class:

raise EMathError.Create(' ');

But it is possible to separate the creation and excitation of an exceptional situation:

var E: EMathError;
begin
E: = EMathError.Create С');
raise E;
end;

The raise statement passes the generated exception to the nearest try.. except (see below).

if С = 0 then
raise EDivByZero.Create('Деление на ноль')
else
А: = В/С;

Self-initialization of the IC can be useful when programming the application's response to data input, for controlling the values of variables, etc. In such cases, it is desirable to create your own IC classes, specially adapted to your needs. It is also useful to use specially designed exceptions when creating your own objects and components. Thus, many of the most important VCL classes – lists, streams, graphical objects – signal their (or yours?) problems by creating the corresponding IC – EListErrorEInvalidGraphicEPrinter, etc.

The most important distinguishing feature of an Exception object is still the class to which it belongs. It is the fact that the IP that has arisen belongs to a particular class that speaks to what happened. If you want to drill down into the problem, you can assign a value to the Message property. If this is not enough, you can add new fields to the object. Thus, in the EinOutError IC (I/O error) there is an ErrorCode field, the value of which corresponds to the error that occurred - the prohibition of writing, the absence or damage of the file, etc.

try
.FileOpenC c:\myfile.txt', fmOpenWrite);
except
on E: EinOutError do
case E.ErrorCode of
ERROR_FILE_NOT_FOUND {=2}: ShowMessage('File not found!');
ERROR_ACCESS_DENIED {=5}: ShowMessage('Access denied!');
ERROR_DISK_FULL {=112}: ShowMessage('Disk is full!');
end;
end;

However, EInOutError ICs occur only when the {$IOCHECKS ON} compiler option is set (or otherwise {$I+}). Otherwise, you need to check the IOResult variable (known from Turbo Pascal) yourself.

An even more "advanced" example is the EDBEngineError IC. It has ErrorCount properties and an Errors array property: a single database operation can generate several errors at once.