Любое завершение выполнения программы по причинам, отличным от достижения конца try-блока и "проваливания вниз" или выполнения оператора __leave, считается аварийным завершением. Результатом выполнения оператора __leave является переход в конец блока __try и передача управления вниз по тексту программы, что намного эффективнее простого использования оператора goto, поскольку не требует разворачивания стека. Для определения того, каким образом завершилось выполнение try-блока, в обработчике завершения используется следующая функция:
BOOL AbnormalTermination(VOID)
При аварийном завершении выполнения блока эта функция возвращает значение TRUE, при нормальном — FALSE.
Примечание
Завершение будет считаться аварийным, даже если, например, последним оператором try-блока был оператор return.
Выполнение обработчика завершения и выход из него
Обработчик завершения, или блок __finally, выполняется в контексте блока или функции, работу которых он отслеживает. Управление может переходить от оператора завершения к следующему оператору. Существует и другая возможность, когда обработчик завершения выполняет оператор передачи управления (return, break, continue, goto, longjmp или __leave). Еще одной возможностью является выход из обработчика по причине возникновения исключения.
Сочетание блоков finally и except
Один try-блок может иметь только один блок finally или только один блок except, но не может иметь оба указанных блока одновременно. Поэтому нижеприведенный код вызовет появление ошибок на стадии компиляции.
__try {
/* Блок контролируемого кода. */
} __except (filter_expression) {
/* Блок обработчика исключений. */
} __finally {
/* Так делать нельзя! Это приведет к ошибке на стадии компиляции. */
}
Вместе с тем, допускается вложение одного блока в другой, что используется довольно часто. Нижеприведенный код является вполне работоспособным и обеспечивает гарантированное удаление временных файлов при выходе из цикла под управлением программы или в результате возникновения исключения. Эта методика оказывается удобной и в тех случаях, когда требуется обеспечить гарантированную отмену блокирования файлов, что будет использовано в программе 4.2. Кроме того, в коде имеется внутренний блок try…except, размещенный в том месте программы, где выполняются вычисления, в которых участвуют вещественные числа.
__try { /* Внешний блок try-except. */
while (…) __try { /* Внутренний блок try-finally. */
hFile = CreateFile(TempFile, …);
if(…) __try { /* Внутренний блок try-except. */
/* Разрешить FP-исключения. Выполнить вычисления. */
…
} __except(EXCEPTION_EXECUTE_HANDLER) {
… /* Обработать FP-исключение. */
_clearfp();
}
… /* Обработка исключений, не являющихся FP-исключениями. /*
} __finally { /* Конец цикла while. */
/* Выполняется на КАЖДОЙ итерации цикла. */
CloseHandle(hFile);
DeleteFile(TempFile);
}
} __except (
/* Обработчик исключений. */
}
Глобальное и локальное разворачивание стека
Исключения и аварийные завершения вызывают
Вспомните, что структура стека является динамической, как показано на рис. 4.1, и что в стеке, наряду с другими данными, хранятся данные обработчиков исключений и завершения. Фактическое содержимое стека в любой момент времени зависит от следующих факторов:
•
•
Обработчики завершения: завершение процессов и потоков