Структурированная обработка исключений (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++

<exception>

Основные понятия

Обработка ошибок и исключений (современный C++)

Другие ресурсы

Структурированная обработка исключений (Windows)