Как компилятор C++ реализует обработку исключений - Структурная обработка исключений - обзор
ОГЛАВЛЕНИЕ
Структурная обработка исключений - обзор
Будут рассматриваться исключения, явно выбрасываемые или возникающие из-за таких условий, как деление на ноль или обращение к нулевому указателю. Когда возникает исключение, генерируется прерывание, и управление передается операционной системе. Операционная система, в свою очередь, вызывает обработчик исключения, просматривающий последовательность вызовов функций, начиная с текущей функции, из которой возникло исключение, и выполняет свою работу раскрутки стека и передачи управления. Можно написать свой собственный обработчик исключений и зарегистрировать его в операционной системе, чтобы он вызывался при исключении.
Windows определяет специальную структуру для регистрации, называемую EXCEPTION_REGISTRATION:struct EXCEPTION_REGISTRATION
{
EXCEPTION_REGISTRATION *prev;
DWORD handler;
};
Чтобы зарегистрировать ваш собственный обработчик исключений, создайте эту структуру и сохраните ее адрес в нулевом смещении сегмента, на который указывает регистр FS, как показывает следующая команда на языке псевдоассемблера:
mov FS:[0], exc_regp
поле prev обозначает связанный список структур EXCEPTION_REGISTRATION. При регистрации структуры EXCEPTION_REGISTRATION адрес ранее зарегистрированной структуры сохраняется в поле prev.
Как выглядит функция обратного вызова исключения? Windows требует, чтобы сигнатура обработчика исключения, определенная в EXCPT.h, представляла собой:
EXCEPTION_DISPOSITION (*handler)(
_EXCEPTION_RECORD *ExcRecord,
void * EstablisherFrame,
_CONTEXT *ContextRecord,
void * DispatcherContext);
Пока можно игнорировать все параметры и тип возвращаемого значения. Следующая программа регистрирует обработчик исключения в операционной системе и генерирует исключение, пытаясь разделить на ноль. Это исключение захватывается обработчиком исключения, который делает немногое. Он лишь выводит сообщение и завершает работу.
#include <iostream>
#include <windows.h>
using std::cout;
using std::endl;
struct EXCEPTION_REGISTRATION
{
EXCEPTION_REGISTRATION *prev;
DWORD handler;
};
EXCEPTION_DISPOSITION myHandler(
_EXCEPTION_RECORD *ExcRecord,
void * EstablisherFrame,
_CONTEXT *ContextRecord,
void * DispatcherContext)
{
cout << "В обработчике исключения" << endl;
cout << "Только демо. выходим..." << endl;
exit(0);
return ExceptionContinueExecution; //не дойдет досюда
}
int g_div = 0;
void bar()
{
//инициализируем структуры EXCEPTION_REGISTRATION
EXCEPTION_REGISTRATION reg, *preg = ®
reg.handler = (DWORD)myHandler;
//получаем текущую голову цепочки обработки исключений
DWORD prev;
_asm
{
mov EAX, FS:[0]
mov prev, EAX
}
reg.prev = (EXCEPTION_REGISTRATION*) prev;
//регистрируем ее!
_asm
{
mov EAX, preg
mov FS:[0], EAX
}
//генерируем исключение
int j = 10 / g_div; //Исключение. Деление на 0.
}
int main()
{
bar();
return 0;
}
/*-------вывод-------------------
В обработчике исключения
Только демо. Выходим...
---------------------------------*/
Windows строго следит за соблюдением одного правила: структура EXCEPTION_REGISTRATION должна быть на стеке и должна быть в более младшем адресе ячейки памяти, чем ее предыдущий узел. Windows завершит процесс, если не обнаружит, что вышеупомянутое правило соблюдено.