Процедуры выхода
В помощью процедур выхода (или процедур завершения) вы може- те управлять процессом завершения работы программы. Это полезно в том случае, когда вы хотите перед прекращением работы программы обеспечить выполнение определенных действий (типичным примером является обновление и закрытие файлов).
Реализовать процедуру выхода вам позволяет переменная-указа- тель EхitProc. Процедура выхода всегда получает вызов при завер- шении работы программы, независимо от того, является ли это за- вершение нормальным окончанием работы программы, завершением после обращения к функции Наlt, или работа программы прекратилась из-за ошибки во время выполнения.
Параметры для процедуры выхода не требуются, и для того, чтобы использовался дальний тип вызова, она должна компилировать- ся с указанием директивы компилятора {$F+}.
Когда процедура выхода должным образом реализована, она в действительности становится частью цепочки процедур выхода. Эта цепочка позволяет реализовать процедуры выхода как для модулей, так и для программ. В некоторых модулях процедура выхода реализу- ется, как часть самого модуля, а выполнение некоторых завершающих действий после выхода из модуля, например, закрытие файлов или восстановление векторов прерываний, возлагается на конкретную процедуру. Процедуры в цепочке выхода выполняются в последова- тельности, обратной порядку их реализации. Этим обеспечивается, что операторы выхода одного блока не выполняются, пока не будут выполнены операторы выхода какого-либо зависящего от него модуля.
Чтобы сохранить цепочку выхода в неприкосновенности, вы должны перед изменением указателя EхitPrос на адрес вашей собс- твенной процедуры сохранить текущее содержимое этого указателя. Далее, непосредственно перед возвратом управления ваша процедура выхода должна восстановить сохраненное значение EхitProc. В сле- дующей программе показаны основы метода реализации такой процеду- ры выхода.
program Testexit; var ExitSave: Pointer;
procedure MyExit; far
begin ExitProc := ExitSave; { всегда восстанавливает сначала старый вектор } . . . end;
begin ExitProc := ExitSave; ExitProc := @MyExit; . . . end.
При входе в программу содержимое EхitProc сохраняется с EхitSave, а затем следует процедура выхода МуEхit. После того, как она будет вызвана в качестве элемента процесса завершения ра- боты программы, процедура МуEхit восстановит предыдущую процедуру выхода.
Программа завершения в библиотеке исполняющей системы будет вызывать процедуры выхода, пока указатель EхitPrос не примет зна- чение nil. Во избежании зацикливания EхitPrос устанавливается в nil перед каждым обращением, так что следующая процедура выхода вызывается только в том случае, если текущая процедура выхода ус- танавливает для EхitPrос ее адрес. Если при выполнении процедуры выхода возникает ошибка, то в ней не успеет еще выполниться прис- ваивание нового адреса указателю EхitPrос, так как это делается непосредственно перед тем, как процедура выхода выполнит возврат управления.
Процедура выхода может распознавать причину завершения рабо- ты программы путем проверки целочисленной переменной EхitCode и переменной-указателя ErrorAddr. В случае нормального завершения в EхitCode содержится нулевое значение и ErrorAddr имеет значение nil. В случае завершения через обращение к процедуре Наlt EхitCode содержит значение, переданное функции Наlt, а ErrorAddr имеет значение nil. Наконец, в случае прекращения работы програм- мы из-за ошибки во время ее выполнения EхitCode содержит код ошибки, а ErrorAddr содержит адрес ошибочного оператора.
Примечание: О процедурах выхода для DLL рассказывается в Главе 11.
Последняя процедура выхода (которая содержится в библиотеке исполняющей системы) закрывает файлы Input и Output и восстанав- ливает векторы прерываний, которые были перехвачены Турбо Паска- лем. При этом, если указатель ErrorAddr имеет значение, отличное от nil, то процедура выхода выводит сообщение об ошибке во время выполнения программы. Если вы хотите выводить свои собственные сообщения об ошибках во время выполнения, используйте процедуру выхода, которая проверяет ErrorAddr и выводит сообщение об ошиб- ке, если его значение отлично от nil. В добавок к этому перед возвратом управления необходимо обеспечить, чтобы указатель ErrorAddr был установлен в значение nil, чтобы сообщение об ошиб- ке не выдавалось снова другой процедурой выхода.
После того, как библиотека исполняющей системы обращается в процедурам выхода, она возвращает управление DOS и передает в ка- честве кода возврата значение, содержащееся в ЕхitCode.