Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

SEH исключения VS С++ исключения


Домашняя страница www.devdoc.ru

DevDoc - это новые статьи по программированию каждую неделю.

Заходи и читай!

Домашняя страница Письмо автору Архив рассылки Публикация статьи

Выпуск №21

Здравствуйте уважаемые подписчики, сегодня в номере:

  • Статья "SEH исключения VS С++ исключения"

Постоянная ссылка на статью (с картинками): http://www.devdoc.ru/index.php/content/view/seh_vs_cpp.htm

Автор: Кудинов Александр
Последняя модификация: 2007-05-30 19:06:56

SEH исключения VS С++ исключения

Введение

SEH исключения - это часть операционной системы и теоретически могут использоваться из любой программы. К сожалению, Microsoft не предоставляет документации по реализации исключений, поэтому их можно использовать, только если поддержка встроена в компилятор языка. Зная особенности реализации, можно добавить SEH в любую программу.

Исключения C++, с другой стороны, являются частью языка и поддерживаются всеми компиляторами. Особенности реализации также не описаны в стандарте и остаются полностью на откуп разработчикам компиляторов.

Разрабатывая приложения на C++ необходимо использовать именно средства языка. В документации к MS VC и в Интернете есть неоднократные упоминания, что смешивать SHE и C++ исключения не рекомендуется. С практической точки зрения использование средств языка более предпочтительно, т.к. при обработке исключений и раскрутке стека вызываются деструкторы объектов.

Описание SEH можно найти в статьях: «Введение в обработку структурированных исключений SEH», «Структурная обработка исключений (SEH) в примерах. Часть 1» и «Структурная обработка исключений (SEH) в примерах. Часть 2». В этой статье мы подробнее будем рассматривать C++ исключения вместе с SEH.

С++ исключения изнутри

Рассматривая ассемблерный код фреймов C++ исключений можно заметить, что в компиляторе от MS реализация исключений сделана через SEH. Например, блок try превращается в __try, а catch в __except. Оператор throw превращается в RaiseException. Значение переменной оператора throw передается через аргумент функции RaiseException.

try
{ 
throw int(1); 
} 
catch(int)
{
}

На псевдокоде это можно записать с использованием SEH:

__try
{
 RaiseException(OxE06D7363, EXCEPTION_NONCONTINUABLE, PARAMETERS(1));
}
__except(PARAMETER == int ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SFARCH)
{
}

Разработчики MS VC используют код OxE06D7363 для того, чтобы выделить C++ исключения. Обратите внимание, что возобновить выполнение программы в точке возбуждения исключения невозможно. Способ передачи типов и значений операндов throw не документирован. Однако не трудно догадаться, как это происходит.

Оператор __except выполняет сравнение типа исключения. Если тип совпадет с нужным – возвращается значение EXCEPTION_EXECUTE_HANDLER и выполняется тело обработчика. Если тип не совпал, -происходит поиск другого обработчика исключения, который способен обработать указанный тип.

Теперь, когда вы знаете, как реализованы исключения, можно смешивать оба эти механизма в рамках одной программы. Использование SEH, на мой взгляд, целесообразно, если надо возобновлять выполнение программы с прерванного места. Т.е. фильтр исключения возвращает константу EXCEPTION_CONTINUE_EXECUTION. В остальных случаях можно обойтись с помощью встроенного в язык механизма исключений try/catch.

Такой подход особенно интересен, потому что есть способ перехватывать SEH исключения с помощью try/catch.

Перехват структурных исключений (SEH) с помощью try/catch

В MS VC можно перехватить структурные исключения с помощью следующего кода:

try
{
 *(char*)0 = 10;
}
catch()
{
 //сработает этот код при попытке записи по нулевому адресу.
}

Такой синтаксис предотвратит крах программы. Но, к сожалению, мы не можем получить доступ к кодам исключений и его контексту.

C++ - это очень гибкий язык, а разработчики Майкрософта позаботились о том, чтобы максимально облегчить совместное использование SEH и встроенных в язык исключений. Работает это следующим образом. Каждый раз, когда возникает структурное исключение – вызывается специальная функция «транслятор». Ей передаются результаты работы функций GetExceptionCode и GetExceptionInformation. Дальше эта функция может «запаковать» эти значения вовнутрь класса и выкинуть его в виде C++ исключения. По-умолчанию, в системе не установлена функция-транслятор. Ее можно установить с помощью библиотечной функции _set_se_translator.

Вот как это делается:

// crt_settrans.cpp
// compile with: /EHsc
#include <stdio.h>
#include <windows.h>
#include <eh.h>
 
void SEFunc();
void trans_func( unsigned int, EXCEPTION_POINTERS* );
 
//Класс-обертка, которая хранит в себе информацию об исключении.
class SE_Exception
{
private:
EXCEPTION_RECORD m_er; 
CONTEXT m_context; 
public:
     SE_Exception(PEXCEPTION_POINTERS pep) 
{
m_er = *pep->ExceptionRecord; 
m_context = *pep->ContextRecord;
}
     ~SE_Exception() {}
};
 
int main( void )
{
    try
    {
        _set_se_translator( trans_func );
        SEFunc();
    }
    catch( SE_Exception e )
    {
        printf( "Caught a __try exception with SE_Exception.\n" );
    }
}
void SEFunc()
{
    __try
    {
        int x, y=0;
        x = 5 / y;
    }
    __finally
    {
        printf( "In finally\n" );
    }
}
void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp )
{
    printf( "In trans_func.\n" );
    throw SE_Exception(pExp);
}

Рассмотрим работу этой программы по шагам:

  1. Устанавливаем функцию-транслятор с помощью _set_se_translator.
  2. Вызывается SEFunc
  3. Генерируется исключение
  4. Выполняется функция-транслятор, которая переводит SEH в C++ исключение.
  5. По правилам SEH выполняется блок __finaly
  6. Выполняется блок catch с типом SE_Exception. Т.е. блоку catch становятся доступны параметры SEH исключения.

Обратите внимание, что функция-транслятор должна устанавливаться для каждого потока в программе.

Как видим, с помощью C++ исключений можно перехватывать SEH. Поэтому в большинстве случаев можно обойтись исключениями C++. Они обладают достаточной мощью, чтобы перехватывать аппаратные и программные SEH исключения. Такой подход по-прежнему не позволяет перезапускать программу с инструкции, которая вызвала исключение. Перезапуск программы может потребоваться в очень редких случаях, поэтому в контексте повседневной обработки ошибок это не актуально. Для тех, кому требуется такая функциональность, могут смешивать SEH и C++ исключения. Это вполне допустимо, потому что теперь вы знаете, как они устроены изнутри.


Если вам нравиться эта рассылка рекомендуйте ее своим друзьям. Подписаться можно по адресу http://subscribe.ru/catalog/comp.soft.prog.devdoc

Copyright (C) Kudinov Alexander, 2006-2007

Перепечатка и использование материалов запрещена без писменного разрешения автора.


В избранное