^ Если для принятия решения об обработке исключения требуется более де-тально обработать информацию об исключении, то в выражении-фильтре используют функцию, которая в этом случае называется функцией фильтра. В функции фильтра не разрешается вызывать функции GetExceptionCode и GetExceptionInformation, однако эти функции могут вызываться для инициализации параметров функции фильтра.
В листинге 2.12 приведена программа, в которой используется функция фильтра для принятия решения о дальнейшей обработке исключения. В этой программе функция фильтра возвращает одно из двух значений EXCEPTION_CONTINUE_EXECUTION или EXCEPTION_EXECUTE_HANDLER. Первое значение возвращается в случае исключения, которое генерируется системой при целочисленном делении на ноль, а второе — в остальных случаях.
^
#include
#include
DWORD filter_function (DWORD ec, int &a) {
// проверяем код исключения
if(ее == EXCEPTION_INT_DIVIDE_BY_ZERO) {
cout <<"Integer divide by zero exception." << endl;
cout <<"a = " < // восстанавливаем ошибку
a = 10;
// возобновляем выполнение программы
cout << "Continue execution." << endl; cout << "a = " << a << endl;
return EXCEPTION_CONTINUE_EXECUTION;
}
else
// прекращаем выполнение программы
return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{
int а = 0; int Ъ = 1000;
__try {
Ь /= а;
cout <<"b = " << b < }
except(filter_function(GetExceptionCode() , a))
{
cout <<"There was some exception." < }
return 0;
} Отметим, что в общем случае возобновление программы выполнить довольно сложно, т .к. процессор повторяет попытку выполнения программы с машинной команды, на которой произошло исключение. Естественно, что эта машинная команда не обязательно соответствует инструкции на языке высокого уровня, которая вызвала исключение.
^ Более подробную информацию об исключении можно получить при помощи вызова функции GetExceptionlnformation, которая имеет следующий прототип:
LPEXCEPTION_POINTERS GetExceptionlnformation(VOID);
Эта функция возвращает указатель на структуру типа:
typedef struct EXCEPTION_POINTERS {
PEXEPTION_RECORD ExceptionRecord;
PCONTEXT Context;
^
которая, в свою очередь, содержит два указателя: ExceptionRecord и Context на структуры типа EXCEPTION_RECORD и CONTEXT соответственно.
В структуру типа CONTEXT система записывает содержимое всех регистров процессора на момент исключения. Эта структура имеет довольно громоздкое описание, которое можно найти в заголовочном файле WinNt.h.
Структура типа EXCEPTION_RECORD имеет следующий формат:
typedef struct EXCEPTION_RECORD{
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
}EXCEPTION_RECORD,*PEXCEPTION_RECORD;
В нее система записывает информацию об исключении. Поля этой структуры имеют следующее назначение.
Поле ExceptionCode содержит код исключения, который может принимать такие же значения, как и код исключения, возвращаемый функцией GetExceptionCode.
Поле ExceptionFlags может принимать одно из двух значений:
0 – которое обозначает, что после обработки исключения возможно во-зобновление выполнения программы;
EXCEPTION_NONCONTINUABLE – которое обозначает, что после обработки исключения возобновление выполнения программы невозможно.
Если установлено значение EXCEPTION_NONCONTINUABLE и выполнена попытка возобновления выполнения программы, то система выбросит исключение EXCEPTION_NONCONTINUABLE_EXCEPTION.
Поле ExceptionRecord содержит указатель на следующую структуру типа EXCEPTION_RECORD, которая может быть создана в случае вложенных исключений.
Поле ExceptionAddress содержит адрес инструкции в программе, на которой произошло исключение.
Поле NumberParameters содержит количество параметров, заданных в поле ExceptionInformation, которое является последним в этой структуре.
Поле ExceptionInformation [ EXCEPTION_MAXIMUM_PARAMETERS] определяет массив 32-битных аргументов, которые описывают исключение. Элементы этого массива могут использоваться функцией генерации программных исключений RaiseException. Отметим, что в операционных системах Windows NT/2000 для исключения с кодом EXCEPTION_ACCESS_VIOLATION определены значения первых двух элементов этого массива. В этом случае первый элемент массива содержит одно из значений:
0 — попытка чтения виртуальной памяти;
1 — попытка записи в виртуальную память.
А второй элемент массива содержит адрес виртуальной памяти, по которому программа пыталась прочитать или записать данные.
Сделаем важное замечание о том, что функция GetExceptionInformation может вызываться только в выражении фильтра. Поэтому эта функция вы-зывается всегда только в том случае, если исключение произошло. Кроме того, структуры типа EXCEPTION_POINTERS, EXCEPTION_RECORD и CONTEXT действительны только на время вычисления выражения-фильтра. Чтобы использовать содержимое структур типа EXCEPTION_RECORD и CONTEXT в блоке обработки исключения, его нужно сохранить в объявленных в программе переменных такого же типа. Как видно из описания структуры EXCEPTION_RECORD, функцию GetExceptionInformation можно использовать для двух целей: первая цель заключается в получении более подробной ин-формации об исключении, учитывая содержимое структуры типа CONTEXT; вторая цель состоит в обработке вложенных исключений.
Теперь приведем, в листинге 2.13, программу, которая выводит на консоль информацию об исключении, используя для получения этой информации функцию GetExceptionInformation. Листинг 2.13. Получение информации об исключении
#include
#include EXCEPTION_RECORD er; // информация об исключении DWORD filter_function(EXCEPTION_POINTERS *p) {
// сохраняем содержимое структуры EXCEPTION_RECORD er = *(p->ExceptionRecord); return EXCEPTION_EXECUTE_HANDLER;
} int main() {
int *p = NULL; // пустой указатель на целое число __try {
*р = 10; // ошибка, так как пустой указатель
}
__except(filter_function(GetExceptionlnformation()))
{
// распечатываем информацию об исключении
cout <<"ExceptionCode = " << er.ExceptionCode < cout <<”ExceptionFlags = " < cout <<"ExceptionRecord = " << er.ExceptionRecord << endl;
cout <<"ExceptionAddress = " << er.ExceptionAddress << endl;
cout <<"NumberParameters = " << er. Number Parameters << endl;
// распечатываем параметры
if(er.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
cout << "Type of access = " << er.ExceptionInformation[0] << endl;
cout << "Address of access = " << er.Exceptionlnformation[1] << endl;
}
cout << endl;
return 0;
}
|