Re[2]: Почему не обрабатывается исключение:
От: LaptevVV Россия  
Дата: 25.09.06 06:16
Оценка: 1 (1)
Здравствуйте, a111, Вы писали:

A>ммм понятно

A>А делфи через SEH работает с исключениями?
A>Просто там как-то проще. В С++ долго разбирался и то, не до конца разобрался с ними.
Смотри сюда — первоначальный ликбез

Структурная обработка исключений (SEH) реализована и в Visual C++.NET 2003 и в C++ Builder 6 как расширение стандартного С++. Поддержка SEH обеспечивается операционной системой Windows, поэтому программы, в которых используется SEH, непереносимы. Кроме того, при структурной обработке исключений не выполняется вызов деструкторов — это наиболее важное отличие SEH от стандартного механизма. Поэтому Microsoft не рекомендует смешивать стандартные и структурные исключения в одной программе.
Структурная обработка исключений предоставляет две возможности: обработку исключений try/except и обработку завершения try/finally. Обработка завершения проще, поэтому начнем с нее. В системе Visual C++.NET 2003 синтаксис обработчика завершения выглядит так:

__try { }            // защищенный блок
__finally { }        // блок завершения

Ключевые слова __try и __finally пишутся с двумя подчеркиваниями — обычно в системе Visual C++.NET 2003 так обозначаются все расширения стандартного С++.
Обработчик завершения гарантирует, что блок завершения будет выполнен при любой попытке выхода из защищенного блока — независимо от способа выхода. Выход из __try-блока осуществляется одним из следующих способов:
1. нормальное выполнение всех операторов блока от начала до конца;
2. возникновение исключения во время выполнения операторов блока;
3. выполнение одного из операторов перехода (break, continue, goto, return), хотя этого Джеффри Рихтер [49] рекомендует избегать.
Поведение программы после выполнения финального блока зависит от способа выхода из __try-блока. Если в финальном блоке нет никаких операторов перехода, и он выполняется до конца, то далее в соответствии со способом выхода из защищенного блока:
1. выполняются операторы после финального блока;
2. выполняются операторы после финального блока;
3. выполняется оператор перехода, который вызвал выход из защищенного блока.
Синтаксис обработчика исключений выглядит так:
__try { }            // защищенный блок
__except (фильтр)
{ }
// блок обработки исключения
Обратите внимание, что в SEH после защищенного блока следует единственный обработчик, — либо обработчик завершения, либо обработчик исключения. Несколько обработчиков писать нельзя, и тем более нельзя писать несколько разных обработчиков. Однако разрешается конструкцию try/finally вкладывать в try/except и наоборот, и уровень вложенности не ограничен. Правда, в этом случае существует опасность «запутаться» в порядке выполнения блоков __except и __finally.
При нормальном ходе выполнения программы (когда исключения не возникает) после выполнения операторов защищенного блока блок-обработчик пропускается, и программа продолжает работу со следующего после него оператора. А вот если при выполнении операторов защищенного блока возникло исключение, то обработка его зависит от фильтра. Фильтр — это выражение (в том числе и вызов функции), которое должно принимать одно из трех возможных значений:
    EXCEPTION_CONTINUE_EXECUTION (–1);
    EXCEPTION_CONTINUE_SEARCH (0);
    EXCEPTION_EXECUTE_HANDLER (1);

Очень часто эти константы прописываются в качестве фильтра непосредственно. Только последний вариант означает собственно обработку исключения: выполняются операторы блока обработки, после чего управление передается на первый оператор после него.
Если результат вычисления фильтра равен первой константе, то исключение отклоняется (exception is dismissed). Код в обработчике прерываний никогда не выполняется. Управление возвращается в точку возникновения прерывания и делается попытка снова выполнить ту же инструкцию, которая вызвала исключение.
На первый взгляд поведение не совсем логичное, ведь снова возникнет исключение. Однако не будем забывать, что фильтр – это выражение. Это означает, что на месте фильтра может быть прописано несколько выражений через запятую. В этих выражениях можно исправить причину, вызвавшую исключение. Если необходимо выполнить более сложную работу, то в качестве фильтра может быть прописан вызов функции. Эта функция может получить в качестве параметров любые переменные из защищенного блока, выполнить с ними нужную работу и возвратить константу EXCEPTION_CONTINUE_EXECUTION в качестве результата. Например, если произошло деление на ноль, то функция может изменить значение делителя, и программа сможет «хромать» дальше.
Фильтр EXCEPTION_CONTINUE_SEARCH по своему действию похож на оператор throw без аргумента, то есть «отправляет исключение дальше». Код в обработчике с таким фильтром не выполняется никогда.
Генерация программных исключений выполняется функцией Windows API RaisException(), которую мы рассматривать не будем — подробное изучение SEH в нашу задачу не входит. Однако на одном моменте нужно остановиться. Чем хорошо SEH, так это возможностью «отловить» аппаратные исключения и точно их идентифицировать, естественно, с помощью функции API
DWORD GetExceptCode();
Функция возвращает идентификатор типа исключения, которые определены в файле WinBase.h. Но подключать надо заголовок windows.h. В табл. 4.1 представлены некоторые из этих идентификаторов.

Таблица 4.1. Аппаратные исключения
Значение Описание
EXCEPTION_ACCESS_VIOLATION    //Попытка обратиться к «чужой» памяти
EXCEPTION_FLT_DIVIDE_BY_ZERO    //Деление на ноль дробных чисел
EXCEPTION_FLT_OVERFLOW            //Переполнение для дробных чисел
EXCEPTION_INT_DIVIDE_BY_ZERO    //Деление на ноль для целых чисел
EXCEPTION_INT_OVERFLOW            //Переполнение для целых чисел
EXCEPTION_STACK_OVERFLOW    //Переполнение стека

В составе Windows API реализованы и другие функции, используемые в SEH. Останавливаться подробно мы на этом не будем, так как это не входит в нашу задачу. В справочной системе Visual C++.NET 2003 прописан пример, в котором модифицированы операторы ввода/вывода (листинг 4.22).
//Листинг 4.22. Структурная обработка исключений
// exceptions_try_except_Statement.cpp
// Пример try-except и try-finally 
#include <iostream>
#include <windows.h> // для EXCEPTION_ACCESS_VIOLATION
using std::cout;
using std::endl;
// функция-филтр
int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
   cout<<"in filter."<< endl;
   if (code == EXCEPTION_ACCESS_VIOLATION) {
      cout<<"caught AV as expected."<<endl;
      return EXCEPTION_EXECUTE_HANDLER;            // если авария
   }
   else {
      cout<<"didn't catch AV, unexpected."<< endl;
      return EXCEPTION_CONTINUE_SEARCH;            // если нет аварии
   };
}
int main()
{   int* p = 0x00000000;           // специально-нулевой указатель
   cout<<"hello"<< endl;
   __try{
      cout<<"in try 1"<< endl;
      __try{
         cout<<"in try 2"<< endl;
         *p = 13;                // access violation 
      }__finally{
         cout<<"in finally. termination: "<< endl;
         cout<<(AbnormalTermination() ? "\tabnormal" : "\tnormal")<< endl;
      }
   }__except(filter(GetExceptionCode(), GetExceptionInformation())){
      cout<<"in except"<< endl;
   }
   cout<<"world"<< endl;
}

Программа специально содержит ошибку (нулевой указатель), чтобы показать обработку возникающего исключения по нарушению доступа к памяти (access violation). Демонстрируется также обязательное выполнение финального блока, несмотря на исключение. Программа выводит на экран
hello
in try 1
in try 2
in filter.
caught AV as expected.
in finally. termination:
        abnormal
in except
world

SEH очень подробно описана у Джеффри Рихтера.

Хочешь быть счастливым — будь им!
Без булдырабыз!!!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.