Структурированная обработка исключений (C/C++)
Хотя Windows и Visual C++ поддерживают структурированную обработку исключений (SEH), настоятельно рекомендуется использовать обработку исключений C++ в соответствии со стандартом ISO, поскольку это повышает переносимость и гибкость кода. Однако в имеющемся коде или в программах определенных видов можно продолжать использовать SEH.
Грамматика
try-except-statement :
__try compound-statement
__except ( expression ) compound-statement
Заметки
Обработка ошибок SEH позволяет в случае непредвиденного завершения программы корректно освободить все ресурсы, например блоки памяти и файлы. Кроме того, она позволяет обрабатывать конкретные проблемы, например нехватку памяти, при помощи краткого структурированного кода, который не зависит от операторов goto или длительного тестирования кодов возврата.
Операторы try-except и try-finally, которые упоминаются в этом разделе, являются расширениями языка C для систем Microsoft. Они поддерживают SEH, позволяя приложениям получать контроль над программой после событий, которые в иных ситуациях привели бы к завершению выполнения. Хотя обработка ошибок SEH работает с исходными файлами C++, она не была создана специально для этого языка. Если SEH используется в программе на C++, которая компилируется с параметром /EH (вместе с некоторыми модификаторами), то деструкторы для локальных объектов вызываются, однако прочие аспекты выполнения могут отличаться от ожидаемого. (См. пример в этом разделе.) В большинстве случаев вместо SHE рекомендуется использовать стандартную обработку исключений C++ согласно стандарту ISO, которая также поддерживается Visual C++. С помощью обработки исключений C++ можно повысить переносимость кода и обрабатывать исключения любого типа.
При наличии модулей на C, в которых используется SEH, их можно использовать одновременно с модулями на C++, в которых используется обработка исключений C++. Дополнительные сведения см. в разделе Различия в обработке исключений.
Существует два механизма SEH.
Обработчики исключений, которые могут реагировать на исключение или отбрасывать его.
Обработчики завершения, которые вызываются, когда исключение вызывает завершение в блоке кода.
Эти два типа обработчиков различаются, однако тесно взаимодействию в процессе, который называется "развертыванием стека". При возникновении исключения операционная система Windows ищет последний установленный обработчик исключений, который в данный момент активен. Обработчик может выполнить одно из трех действий:
не распознать исключение и передать управление другим обработчикам;
распознать исключение, но отбросить его;
распознать исключение и обработать его.
Обработчик исключений, распознавший исключение, может находиться за пределами функции, которая выполнялась, когда возникло исключение. В некоторых случаях он может находиться в функции, хранящейся гораздо выше по стеку. Выполняемая в настоящее время функция и все прочие функции в кадре стека завершаются. Во время этого процесса стек "развертывается"; иными словами, из него удаляются локальные переменные и завершенные функции, если они не были определены с ключевым словом static.
По мере развертывания стека операционная система вызывает все обработчики завершения, которые были написаны для каждой функции. При помощи обработчиков завершения можно освободить ресурсы, которые в противном случае оставались бы открытыми ненормального завершения. Если был выполнен вход в критическую секцию, обработчик завершения можно использовать для выхода из нее. Если ожидается завершение программы, можно выполнить другие задачи обслуживания, например закрыть и удалить временные файлы.
Дополнительные сведения см. в следующих разделах:
Пример
Как уже говорилось выше, деструкторы для локальных объектов вызываются, если в программе на C++ используется обработка ошибок SEH, а сама программа скомпилирована с параметром /EH с определенными модификаторами, например /EHsc и /EHa. Однако если при этом используются исключения C++, то поведение во время выполнения может отличаться от ожидаемого. Это различие в поведении демонстрируется в следующем примере.
#include <stdio.h>
#include <Windows.h>
#include <exception>
class TestClass
{
public:
~TestClass()
{
printf("Destroying TestClass!\r\n");
}
};
__declspec(noinline) void TestCPPEX()
{
#ifdef CPPEX
printf("Throwing C++ exception\r\n");
throw std::exception("");
#else
printf("Triggering SEH exception\r\n");
volatile int *pInt = 0x00000000;
*pInt = 20;
#endif
}
__declspec(noinline) void TestExceptions()
{
TestClass d;
TestCPPEX();
}
int main()
{
__try
{
TestExceptions();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf("Executing SEH __except block\r\n");
}
return 0;
}
Если при компиляции этого кода установлен параметр /EHsc, однако локальное управление проверкой CPPEX не определено, то деструктор TestClass не выполняется, и вывод выглядит следующим образом:
Если при компиляции кода установлен параметр /EHsc, а CPPEX определено при помощи ключа /DCPPEX (чтобы создавалось исключение C++), то деструктор TestClass выполняется, и вывод выглядит следующим образом:
Если при компиляции кода установлен параметр /EHa, то деструктор TestClass выполняется вне зависимости от того, было ли исключение создано при помощи std::throw или при помощи обработки исключений SEH, которая создавала ошибку (вне зависимости от того, было ли определено CPPEX). Вывод выглядит следующим образом.
Дополнительные сведения см. в разделе Параметр /EH (модель обработки исключений).
См. также
Ссылки
Обработка исключений в Visual C++
Основные понятия
Обработка ошибок и исключений (современный C++)