Windows для профессионалов


Функция GetExceptionCode


Часто фильтр исключений должен проанализировать ситуацию, прежде чем опреде лить, какое значение ему вернуть. Например, Ваш обработчик может знать, что делать при делении на нуль, по не знать, как обработать нарушение доступа к памяти Имен но поэтому фильтр отdечает за анализ ситуации и возврат соответствующего значения Этот фрагмент иллюстрирует метод, позволяющий определять тип исключения:

__try
{

x = 0;

У = 4 / x;

}

__except ((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ? EXCEPTlON_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{

// обработка деления иа нуль

}

Встраиваемая функция GetExceptionCode возвращает идентификатор типа исклю чения.

DWORD GotExceptionCode();

Ниже приведен список всех предопределенных идентификаторов исключений с пояснением их смысла (информация взята из докуметации Platform SDK) Эти иден тификаторы содержатся в заголовочном файле WinBase.h. Я сгруппировал исключе ния по категориям.

Исключения, связанные с памятью

  • EXCEPTION_ACCESS_VIOLATION Поток пьтался считать или записать по виртуальному адресу, не имея на то необходимых прав. Это самое распрост раненное исключение.



  • EXCEPTION_DATATYPE_MISALIGNMENT Поток пытался считать или запи сать невыровненные данные на оборудовании, которое не поддерживает ав томатическое выравнивание. Например, 16-битные значения должны быть вы ровнены по двухбайтовым границам, 32-битные — по четырехбайтовым и т. д,

  • EXCEPTION_ARRAY_ROUNDS_EXCEEDED Поток пытался обратиться к эле менту массива, индекс которого выходит за границы массива; при этом обо рудование должно поддерживать такой тип контроля.

  • EXCEPTION_INPAGE_ERROR Ошибку страницы нельзя обработать, так как файловая система или драйвер устройства сообщили об ошибке чтения.

  • EXCEPTION_GUARD_PAGE Поток пытался обратиться к странице памяти с атрибутом защиты FAGE_GUARD. Страница становится доступной, и генериру ется данное исключение

  • EXCEPTION_STACK_OVERFLOW Стек, отведенный потоку, исчерпан.

  • EXCEPTION_ILLEGAL_INSTRUCTION Поток выполнил недопустимую инст рукцию Это исключение определяется архитектурой процессора; можно ли перехватить выполнение неверной инструкции, зависит от типа процессора.


  • EXCEPTION_PRIV_INSTRUCTION Поток пытался выполнить инструкцию, не допустимую в данном режиме работы процессора.


  • Исключения, связанные с обработкой самих исключений

  • EXCEPTION_INVALID_DISPOSITION Фильтр исключений вернул значение, огличное от EXCEPTION_EXECUTE_HANDLER, EXCEPTION_CONTINUE_SEARCH или EXCEPTION_CONTINUE_FXECUTION.

  • EXCEPTION_NONCONTINUABLEEXCEPTION Фильтр исключений вернул EXCEPTION_CONTINUE_EXECUTION в ответ па невозобновляемое исключение (noncontinuable exception).


  • Исключения, связанные с отладкой

  • EXCEPTION_BREAKPOINT Встретилась точка прерывания (останова).

  • EXCEPTION_SINGLE_STEP Трассировочная ловушка или другой механизм пошагового исполнения команд подал сигнал о выполнении одной команды.

  • EXCEPTION_INVALID_HANDLE В функцию передан недопустимый описатель.


  • Исключения, связанные с операциями над целыми числами

  • EXCEPTION_INT_DIVIDE_BY_ZERO Поток пытался поделить число целого типа на делитель того же типа, равный 0

  • EXCEPTION_INT_OVERFLOW Операция над целыми числами вызвала пере ног старшего разряда результата.


  • Исключения, связанные с операциями над вещественными числами

  • EXCEPTION_FLT_DENORMAL_OPERAND Один из операндов в операции над числами с плавающей точкой (вещественного типа) не нормализован. Ненор мализованными являются значения, слишком малые для стандартного пред ставления числа с плавающей точкой.

  • EXCEPTION_FLT_DIVIDE_BY_ZERO Поток пытался поделить число веще ственного типа на делитель того же типа, равный 0.

  • EXCEPTION_FLT_INEXACT_RESULT Результат операции над числами с пла вающей точкой нельзя точно представить я виде десятичной дроби

  • EXCEPTION_FLT_INVALID_OPERATION Любое другое исключение, относя щееся к операциям над числами с плавающей точкой и нс включенное в этот список

  • EXCEPTION_FLT_OVERFLOW Порядок результата операции над числами с плавающей точкой превышает максимальную величину для указанного типа данных.

  • EXCEPTION_FLT_STACK_CHECK Переполнение стека или выход за его ниж нюю границу в результате выполнения операции над числами с плавающей точкой.



  • EXCEPTION_FLT_UNDERFLOW Порядок результата операции над числами с плавающей точкой меньше минимальной величины для указанного типа дан ных.


  • Встраиваемую функцию GetExceptionCode можно вызвать только из фильтра ис ключений (между скобками, которые следуют за _except) или из обработчика исклю чений. Скажем, такой код вполне допустим:

    __try
    {

    У = 0;

    x = 4 / у;

    }

    _except
    {

    {(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) || (GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO)) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEAHCH)
    {

    switch (GetExceptionCode())
    {

    case EXCEPTION_ACCESS_VIOLATION:

    // обработка нарушения доступа к памяти
    ...
    break;

    case EXCEPTION_INT_DIVIDE_BY_ZERO:

    // обработка деления целого числа на нуль
    ...
    break;

    }

    }

    Однако GetExceptionCode нельзя вызывать из функции фильтра исключений. Ком пилятор помогает вылавливать такие ошибки и обязательно сообщит о таковой, если Вы попытаетесь скомпилировать, например, следующий код:

    __try
    {

    У = 0;

    x = 4 / у;

    }

    __except (CoffeeFilter())
    {

    // обрабогка исключения
    ...

    }

    LONG CoffeeFilter(void)
    {

    // ошибка при компиляции: недопустимый вызов GetExceptionCode

    return((GetExceptionCode() == EXCFPTION_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);

    }

    Нужного эффекта можно добиться, переписав код так:

    __try
    {

    y = 0;
    x = 4 / у;

    }

    __except (CoffeeFi]ter(GetExceptionCode()))
    {

    // обработка исключения
    ...

    }

    LONG CoffeeFilter(DWORD dwExceptionGode)
    {

    return((dwExceptionCode == EXCEPTION_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);

    }

    Коды исключений формируются по тем же правилам, что и коды ошибок, опре деленные в файле WinError.h. Каждое значение типа UWORD разбивается на поля, как показано в таблице 24-1.

    Биты 31-30 29 28 27-16 15-0
    Содержимое Код степени "тяжести" (severity) Кем определен — Microsoft или пользователем Зарезервирован Код подсистемы (facility code) Код исключения
    Значение 0 = успех 1 = информация 2 = предупреждение 3 = ошибка 0 = Microsoft 1 = пользователь Должен быть 0 (см таблицу ниже) Определяется Microsoft Определяется Microsoft или пользовате лем
    <


    Таблица 24-1, Поля кода ошибки

    На сегодняшний день определены такие коды подсистемы.

    Код подсистемы Значение Код подсистемы Значение
    FACILITY_NULL 0 FACILITY_CONTROL 10
    FACILITY_RPC 1 FACILITY_CERT 11
    FACILITY_DISPATCH 2 FACILITY_INTERNET 12
    FACILITY_STORAGE 3 FACILITY_MEDIASERVER 13
    FACILITY_ITF 4 FACILITY_MSMQ 11
    FACILITY_WIN32 7 FACILITY_SETUPAPI 15
    FACILITY_WINDOWS 8 FACILITY_SCARD 16
    FACILITY_SECURITY 9 FACILITY_COMPLUS 17
    Разберем на части, например, код исключения EXCEPTION_ACCESS_VIOLATlON. Если Вы посмотрите его значение в файле WinBase.h, то увидите, что оно равно 0xC0000005:

    С 0 0 0 0 0 0 5 (в шестнадцатеричном виде) 1100 0000 0000 0000 0000 0000 0000 0101 (в двоичном виде)

    Биты 30 и 31 установлены в 1, указывая, что нарушение доступа является ошиб кой (поток не может продолжить выполнение) Бит 29 равен 0, а это значит, что дан ный код определен Microsoft. Бит 28 равен 0, так как зарезервирован на будущее. Биты 16-27 равны 0, сообщая код подсистемы FACILITY_NULL (нарушение доступа может произойти в любой подсистеме операционной системы, а нс в какой-то одной). Биты 0-15 дают значение 5, которое означает лишь то, что Microsoft присвоила исключе нию, связанному с нарушением доступа, код 5.


    Содержание раздела