The following code shows a skeleton implementation of an exit procedure.
var
ExitSave: Pointer;
procedure MyExit;
begin
ExitProc := ExitSave; // always
restore old vector first
.
.
.
end;
begin
ExitSave := ExitProc;
ExitProc := @MyExit;
.
.
.
end.
On entry, the code saves the contents of
ExitProc
in
ExitSave
, then installs the
MyExit
procedure. When called
as part of the termination process, the first thing
MyExit
does is reinstall the previous exit procedure.
The termination routine in the runtime library keeps calling exit procedures until
ExitProc
becomes nilnil. To avoid
infinite loops,
ExitProc
is
set to nil before every call, so the next exit procedure is called only if the current exit
procedure assigns an address to
ExitProc
. If an error occurs in an exit procedure, it is not called again.
An exit procedure can learn the cause of termination
by examining the
ExitCode
integer variable and the
ErrorAddr
pointer variable. In case of normal termination,
ExitCode
is zero and
ErrorAddr
is nil.
In case of
termination through a call to
Halt
,
ExitCode
contains the value passed to Halt and ErrorAddr is nil. In case of
termination due to a runtime error, ExitCode contains the error code and ErrorAddr contains the address of the invalid
statement.
The last exit procedure (the one installed by the runtime library) closes the Input and Output files. If ErrorAddr is not
nil, it outputs a runtime error message. To output your own runtime error message, install
an exit procedure that
examines ErrorAddr and outputs a message if it's not nil; before returning, set ErrorAddr to nil so that the error is
not reported again by other exit procedures.
Once the runtime library has called all exit procedures, it returns
to the operating system, passing the value stored
in ExitCode as a return code.
224