Exit procedures ensure that specific actions such as updating and
closing files are carried out before a program terminates. The ExitProc
pointer variable allows you to install an exit procedure, so that it is always
called as part of the program’s termination whether the termination is normal,
forced by a call to Halt, or the result of a runtime error. An exit
procedure takes no parameters.
Note: It
is recommended that finalization
sections, rather than exit procedures, be used for all exit behavior. Exit
procedures are available only for executables, shared objects (Linux) or .DLL
(Windows) targets; for packages, exit behavior must be implemented in a
finalization section. All exit procedures are called before execution of
finalization sections.
Units as well as programs can
install exit procedures. A unit can install an exit procedure as part of its
initialization code, relying on the procedure to close files or perform other
clean-up tasks.
When implemented properly, an exit
procedure is part of a chain of exit procedures. The procedures are executed in
reverse order of installation, ensuring that the exit code of one unit isn’t
executed before the exit code of any units that depend on it. To keep the chain
intact, you must save the current contents of ExitProc before pointing
it to the address of your own exit procedure. Also, the first statement in your
exit procedure must reinstall the saved value of ExitProc.
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 nil.
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.
退出过程(Exit procedures)确保指定的动作如更新和关闭文件等在程序终止之前被完成。指针变量ExitProc允许在程序中建立一个退出过程,因此它总是作为程序终止的一部分而被调用,不管终止是正常的,被强制调用Halt终止,还是由运行时错误导致的终止。退出过程不接受任何参数。
注意: 推荐利用结束节(finalization
section),它用于所有的退出行为,并且优于退出过程。退出过程仅对可执行文件、共享对象(Linux)或.DLL(Windows)是可用的;对于包,退出行为必须在结束节中实现。所有的退出过程在结束节执行之前被调用。
单元和程序一样可以建立退出过程。单元可以依靠关闭文件或执行其他清除任务的过程作为其结束节的一部分,以此来建立退出过程。
当实现适当时,一个退出过程就是一个退出过程链的一部分。所有退出过程的执行顺序与建立顺序相反,以确保一个单元的退出代码在其他依赖它的单元的退出代码执行之前不被执行。要完整无缺地保持退出过程链,必须在ExitProc被指向自定义退出过程之前保存其当前内容。而且,自定义退出过程中的第一条语句必须重新建立保存过的ExitProc变量的值。
下面的代码说明了一个退出过程的实现框架:
var
ExitSave: Pointer;
procedure
MyExit;
begin
ExitProc := ExitSave; //总是首先恢复旧的向量(退出过程的指针)
...
end;
begin
ExitSave := ExitProc;
ExitProc := @MyExit;
...
end.
在入口,代码保存了在ExitSave中ExitProc的内容,然后建立MyExit作为退出过程。当MyExit作为终止处理的一部分被调用时,它所做的第一件事就是重新建立先前的退出过程。
运行时包中的终止例程保持对退出过程的调用,直到ExitProc成为nil。为避免无限循环,ExitProc在每次调用之前都被置为nil,因此仅当当前退出过程向ExitProc赋予一个非nil的地址时,下一个退出过程被调用。如果在一个退出过程中发生错误,那么退出过程不再被调用。
退出过程可以通过检查整型变量ExitCode和指针变量ErrorAddr来获悉导致终止的原因。正常终止时,ExitCode为零并且ErrorAddr为nil。通过调用Halt终止时,ExitCode包含传给Halt的值且ErrorAddr为nil。因运行时错误终止时,ExitCode包含一个错误代码且ErrorAddr包含了非法语句的地址。
最后的退出过程(被运行时包建立)关闭所有的输入(Input)和输出(Output)文件。如果ErrorAddr不是nil,那么将输出一个运行时错误消息。为输出自定义的运行时错误消息,应建立一个退出过程,该过程检查ErrorAddr并且如果该变量不是nil时输出一个消息;在返回前设置变量ErrorAddr的值为nil,以便错误不再被其他退出过程报告。
一旦运行时库调用了所有的退出过程,那么它将向操作系统返回一个值,即把存储在变量ExitCode中的值作为返回值传递。