Функция 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 элементы.
Например, код внутри фильтра исклю чений может попытаться выполнить деление на нуль. Когда возникает серия вложенных исключений, записи с информацией о них могут образовывать связанный список Исключение будет вложенным, если оно генерируется при обработке фильтра. В отсутствие необработанных исключений ExceptionRecord равен NULL.
Последние два элемента структуры 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,