Коды исключений
Для точной идентификации типа возникшего исключения блок исключения или выражение фильтра могут использовать следующую функцию:
DWORD GetExceptionCode(VOID)
Код исключения должен быть получен сразу же после возникновения исключения. Поэтому функция фильтра не может просто вызвать функцию GetExceptionCode (это ограничение налагается компилятором). Обычный способ решения этой проблемы состоит в том, чтобы осуществить этот вызов в выражении фильтра, как показано в следующем примере, в котором код исключения является аргументом функции фильтра, предоставляемой пользователем:
__except(MyFilter(GetExceptionCode())) {
}
В данном случае значение выражения фильтра, которое должно быть одним из трех указанных ранее значений, определяется и возвращается функцией фильтра. В свою очередь, для определения возвращаемого этой функцией значения используется код исключения; например, можно сделать так, чтобы фильтр передавал обработку исключений, возникающих при выполнении операций с плавающей точкой (FP-исключений, от FloatingPoint — плавающая точка), внешнему обработчику (возвращая значение EXCEPTION_CONTINUE_SEARCH), а обработку нарушений доступа к памяти — текущему обработчику (возвращая значение EXCEPTION_EXECUTE_HANDLER).
Число возможных кодов исключений, возвращаемых функцией GetExceptionCode, очень велико, однако их можно разделить на несколько категорий.
• Выполнение программой некорректных действий, например:
EXCEPTION_ACCESS_VIOLATION — попытка чтения или записи по адресу виртуальной памяти, к которой процесс не имеет доступа.
EXCEPTION_DATATYPE_MISALIGNMENT — многие процессоры, например, требуют чтобы данные типа DWORD выравнивались по четырехбайтовым границам.
EXCEPTION_NONCONTINUABLE_EXECUTION — значением выражения фильтра было EXCEPTION_CONTINUE_EXECUTION, но выполнения программы после возникновения исключения не может быть продолжено.
• Исключения, сгенерированные функциями распределения памяти НеарAlloc и HeapCreate, если они используют флаг HEAP_GENERATE_EXCEPTIONS (см. главу 5). Соответствующими значениями кода исключения являются STATUS_NO_MEMORY или EXCEPTION_ACCESS_VIOLATION.
• Коды определенных пользователем исключений, генерируемых путем вызова функции RaiseException, о чем говорится в подразделе "Исключения, генерируемые приложением".
• Коды различных арифметических исключений (особенно FP-исключений), например, EXCEPTION_INT_DIVIDE_BY_ZERO или EXCEPTION_FLT_OVERFLOW.
• Исключения, используемые отладчиками, например, EXCEPTION_BREAKPOINT или EXCEPTION_SINGLE_STEP.
Вам пригодится также функция GetExceptionInformation, которая может быть вызвана только из выражения фильтра и возвращает дополнительную информацию, включая информацию, специфическую для используемого процессора.
LPEXCEPTION_POINTERS GetExceptionINFORMATION(VOID)
Вся информация, как относящаяся, так и не относящаяся к процессору, содержится в структуре EXCEPTION_POINTERS, состоящей из двух других структур.
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION POINTERS;
В структуру EXCEPTION_RECORD входит элемент ExceptionCode, набор возможных значений которого совпадает с набором значений, возвращаемых функцией GetExceptionCode. Элемент ExceptionFlags структуры EXCEPTION_RECORD может принимать значения 0 или EXCEPTION_NONCONTINUABLE, причем последнее значение указывает функции фильтра на то, что она не должна предпринимать попыток продолжения выполнения. К числу других элементов данных этой структуры относятся адрес виртуальной памяти ExceptionAddress и массив параметров ExceptionInformation. В случае исключения EXCEPTION_ACCESS_VIOLATION значение первого элемента этого массива указывает на то, какая именно из операций пыталась получить доступ по недоступному адресу — записи (1) или чтения (0). Второй элемент содержит адрес виртуальный памяти.
Во втором элементе структуры EXCEPTION_POINTERS, а именно, элементе ContextRecord, содержится информация, относящаяся к процессору. Для каждого типа процессоров предусмотрены свои структуры, определения которых содержатся в файле <winnt.h>.
Резюме: последовательность обработки исключений
На рис. 4.2 в схематическом виде представлена последовательность событий, происходящих после возникновении исключения. Слева приведен программный код, а обведенные кружками цифры справа обозначают операции, выполняемые языковыми средствами поддержки времени выполнения. Отдельные элементы приведенной схемы имеют следующий смысл:
1. Возникло исключение; в данном случае это деление на ноль.
2. Управление передается обработчику исключений, в котором вычисляется выражение фильтра. Сначала вызывается функция GetExceptionCode, а затем ее возвращаемое значение используется в качестве аргумента функции Filter.
3. Функция фильтра выполняет действия, определяемые значением кода исключения.
4. В данном случае значением кода исключения является EXCEPTION_INT_DIVIDE_BY_ZERO.
5. Функция фильтра устанавливает, что должен быть выполнен код обработчика исключений, и поэтому возвращает значение EXCEPTION_EXECUTE_HANDLER.
6. Выполняется код обработчика исключений, связанного с оператором _except.
7. Управление передается за пределы блоков try и except.
Рис. 4.2. Последовательность операций при обработке исключений