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


Функция GetExceptionlnformation


Когда возникает исключение, операционная система заталкивает в стек соответству ющего потока структуры EXCEPTION_RECORD, CONTEXT и EXCEPTION_POINTERS

EXCEPTlON_RECORD содержит информацию об исключении, независимую от типа процессора, a CONTEXT — машинно-зависимую информацию об этом исключении В структуре EXCEPTIONPOINTERS всего два элемента — указатели на помещенные в стек структуры EXCEPTlON_RECORD и CONTEXT.

typedef struct _EXCEPTION_POINTERS
{

PEXCEPTION_RECORD ExceptionRecord;

PCONTEXT ConlexlRecofd;

} EXCEPTTON_POINTERS, *PEXCEPTION_POINTERS;

Чтобы получить эту информацию и использовать ее в программе, вызовите GetEx ceptionInformatton-.

PEXCEPTION_POINTERS GetExceptionInformation();

Эта встраиваемая функция возвращает' указатель на структуру EXCEPTION_POINTERS.

Самое важное в GetExceptionInformatton то, что ее можно вызывать только в филь тре исключений и больше нигде, потому что структуры CONTEXT, EXCEPTION_RE CORD и EXCEPTION_POINTERS существуют лишь во время обработки фильтра исклю чений. Когда управление переходит к обработчику исключений, эти данные в стеке разрушаются.

Если Вам нужно получить доступ к информации об исключении из обработчика, сохраните струкчуру EXCEPTION_RECORD и/или CONTEXT (на которые указывают элементы структуры EXCEPTIONPOINTERS) в объявленных Вами переменных Вот пример сохранения этих структур:

void FuncSkunk()
{

// объявляем переменные, которые мы сможем потом использовать
// для сохранения информации об исключении (если оно произойдет)
EXCEPTION_RECORD SavedExceptRec;
CONTEXT SavedContext;



...

__try
{

...

}

__except
(SavedExceptRec = *(GetExceptionInformation())->ExceptionRecord;
SavedContext = *(GetExceptionInformation())->ContextRecord;
EXCEPTION_EXECUTE_HANDIER)
{

// мы можем теперь использовать переменные SavedExceptRec
// и SavedContext в блоке обработчика исключений
switch (SavedExceptRec ExceptionCode)
{

...

}

}

...

}

В фильтре исключений применяется оператор-запятая (,) — мало кто из програм мистов знает о нсм.
Он указывает компилятору, что выражения, отделенные запяты ми, следует выполнять слева направо После вычисления всех выражений возвраща ется результат последнего из них — крайнего справа.

В FuncSkunk сначала вычисляется выражение слева, что приводит к сохранению находящейся в стеке структуры EXCEPTION_RECORD в локальной переменной Saved ExceptRec. Результат этого выражения является значением SavedExceptRec IIo он от брасывается, и вычисляется выражение, расположенное правее Это приводит к со хранению размещенной в стеке структуры CONTEXT в локальной переменной Saved-

Context. И снова результат — значение SavedContexl — отбрасывается, и вычисляется третье выражение. Оно равно EXCEPTION_EXECUTE_HANDLER — это и будет резуль татом всего выражения в скобках.

Так как фильтр возвращает EXCEPTION_EXECUTE_HANDLER, выполняется код в блоке except. К этому моменту переменные SavedExceptRec и SavedContext уже иници ализированы, и их можно использовать в данном блоке. Важно, чтобы переменные SavedExceptRec и SavedContext были объявлены вне блока try

Вероятно, Вы уже догадались, что элемент ExceptionRecord структуры EXCEP TION_POINTERS указывает на структуру EXCEPTION_RECORD:

typedef struct _EXCEPTION_RECORD
{

DWORD ExceptionCode;
DWORD ExcepLionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NurrberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

Структура EXCEPTIONRECORD содержит подробную машинно-независимую ин формацию о последнем исключении. Вот что представляют собой ec элементы.

  • ExceptionCode — код исключения . Это информация, возвращаемая функцией GetExceptionCode.

  • ExceptionFlags — флаги исключения. На данный момент определено только два значения: 0 (возобновляемое исключение) и EXCEPTION_NONCONTINUABLE (невозобновляемое исключение). Любая попытка возобновить работу програм мы после невозобновляемого исключения генерирует исключение EXCEP TION_NONCONTINUABLE_EXCEPTION.

  • ExceptionRecord - указатель на структуру EXCEPTION_RECORD, содержащую информацию о другом необработанном исключении При обработке одного исключения может возникнуть другое.


    Например, код внутри фильтра исклю чений может попытаться выполнить деление на нуль. Когда возникает серия вложенных исключений, записи с информацией о них могут образовывать связанный список Исключение будет вложенным, если оно генерируется при обработке фильтра. В отсутствие необработанных исключений ExceptionRecord равен NULL.

  • ExceptionAddress — адрес машинной команды, при выполнении которой про изошло исключение

  • NumberParameters — количество параметров, связанных с исключением (0-15). Это число заполненных элементов в массиве ExceptionInformation. Почти для всех исключений значение этого элемента равно 0.

  • ExceptionInformation — массив дополнительных аргументов, описывающих ис ключение. Почти для всех исключений элементы этого массива не определены.


  • Последние два элемента структуры EXCEPTION_RECORD сообщают фильтру до полнительную информацию об исключении. Сейчас такую информацию дает только один тип исключений EXCEPTION_ACCESS_VIOLATION. Все остальные дают нулевое значение в элементе NumberParameters. Проверив его, Вы узнаете, надо ли присмат ривать массив ExceptionInformation.

    При исключении EXCEPTION_ACCESS_VIOLATION эелемент ExceplionInformation[0] содержит флаг, указывающий тип операции, которая вызвала нарушение доступа. Если его значение равно 0, поток пытался читать недоступные ему данные; Т — записы вать данные по недоступному ему адресу. Элемент ExceptionInformation[l] определяет адрес недоступных данных.

    Эта структура позволяет писать фильтры исключений, сообщающие значительный объем информации о работе программы. Можно создать, например, такой фильтр:

    __try
    {

    ...

    }

    __except (ExpFltr(GetExceptionInformation()->ExceptionRecord))
    {

    ...

    }

    LONG ExpFltr(PEXCEPTION_RECORD pER)
    {

    char szBuf[300], *p;
    DWORD dwExceptionCode = pER->ExceptionCode;

    sprintf(szBuf, "Code = %x, Address = %p", dwExceptionCode, pER->ExceptionAddress);

    // находим конец строки
    p = strchr(szBuf, 0);

    // я использовал оператор switch на тот случай, если Microsoft


    // в будущем добавит информацию для других исключений

    switch (dwExceptionCode)
    {

    case EXCEPTION_ACCESS_VIOLATION:

    sprintf(p, "Attempt to %s data at address %p", pER->ExceptionInformation[0] ? "write" : "read", pER->ExceptionInformation[1]);
    break;

    default;

    break;

    }

    MessageBox(NULL, szBuf, "Exception", MB_OK | MB_ICONEXCLAMATION);

    return(EXCEPTION_CONTINUE_SEARCH); }

    Элемент ContextRecord структуры EXCEPTION_POINTERS указывает на структуру CONTEXT (см. главу 7), содержимое которой зависит от типа процессора.

    С помощью этой структуры, в основном содержащей по одному элементу для каж дого регистра процессора, можно получить дополнительную информацию о возник шем исключении. Увы, это потребует написания машинно-зависимого кода, способ ного распознавать тип процессора и использовать подходящую для пего структуру CONTEXT. При этом Вам придется включить в код набор директив #ifdef для разных типов процессоров. Структуры CONTEXT для различных процессоров, поддерживае мых Windows, определены в заголовочном файле WinNT.h,


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