Главная | Обратная связь
МегаЛекции

Установка обработчика верхнего уровня





Давайте немного отдохнем и суммируем все сказанное выше. SEH – это системный сервис, в котором унифицирован механизм обработки исключений, все обработчики текущего потока регистрируются в списке регистрации обработчиков исключений. Если в функции встречается конструкция __try … __except, то создается код, который регистрирует новый обработчик исключения и помещает информацию о нем в стек. Во время завершения функции (а точнее, после того, как управление вышло из секции __try), функция разрегистрирует обработчик. Значит, если к текущему моменту в стеке находится три функции, каждая из которых установила свой обработчик исключения, то в списке обработчиков исключения должно находиться по крайней мере три обработчика, а в стеке должны находиться три записи об обработчиках исключений. Информация о текущем обработчике доступна по адресу fs:[0]. Runtime-библиотека регистрирует свой обработчик исключений, который (если исключение не обрабатывается приложением) вызывает функцию UnhandledExceptionFilter, после чего приложение завершается с выводом диалогового окна «Application Error».

Теперь настало время написать код, который бы использовал сказанное выше и подтвердил правильность наших суждений. Давайте напишем простую функцию, которая бы пробегала по всем зарегистрированным обработчикам и выводила информацию о них на экран. Код функции приведен ниже:

void zWalkThroughSEH() {  _EXCEPTION_REGISTRATION * pVCExcRec;  __asm mov eax, FS:[0]  __asm mov [pVCExcRec], EAX  // Перебираем блоки в связанном списке. 0xFFFFFFFF означает конец списка.  printf("Exception Registration chain:\n");  while (0xFFFFFFFF != (unsigned)(UINT_PTR)pVCExcRec)  {  printf("\tCurrent SEH record: 0x%X\n\tPrev SEH Record: 0x%X\n\tHandler: 0x%X\n\n",  pVCExcRec,  pVCExcRec->prev,  pVCExcRec->hander);  pVCExcRec = (_EXCEPTION_REGISTRATION *)(pVCExcRec->prev);  } }

Вызов эту функции после начала выполнения функции main покажет, что к моменту выполнения функции main в списке обработчиков уже зарегистрированы два обработчика. Текущий обработчик, как было показано раньше, установлен библиотекой Runtime. А вот последний установлен системой.



Exception Registration chain:  Current SEH record: 0x12FFB0  Prev SEH Record: 0x12FFE0  Handler: 0x41123A    Current SEH record: 0x12FFE0  Prev SEH Record: 0xFFFFFFFF  Handler: 0x77E94809

Как вы, наверное, уже догадались, создавать свой обработчик и располагать его за системным нет никакого смысла, поскольку исключение будет обработано в runtime-библиотеке, и приложение будет завершено. Тогда я попытался создать свой обработчик и расположил информацию о нем перед обработчиком runtime-библиотеки, выделив для него место в динамической памяти, но увы, мое приложение просто было выгружено из памяти после возникновения исключения, а вставленный обработчик не был выполнен. Как оказалось, так делать нельзя потому, что все записи списка обработчиков исключений должны лежать в стеке, причем каждая следующая запись должна быть расположена выше предыдущей.

Итак, нельзя расположить информацию об обработчике перед информацией о runtime-обработчике. Но никто не мешает переписать значение поля hander обработчика runtime-библиотеки, установив его так, чтобы он указывал на нашу функцию. Код, который реализует это, приведен ниже.

void zHookUpSEHChain(SEHHandler handler) {  _EXCEPTION_REGISTRATION * pVCExcRec;  __asm mov eax, FS:[0]  __asm mov [pVCExcRec], EAX  // Перебираем блоки в связанном списке. 0xFFFFFFFF означает конец списка.  while (0xFFFFFFFF != (unsigned)(UINT_PTR)pVCExcRec)  {  if ( (unsigned)(UINT_PTR)pVCExcRec->prev->prev == 0xFFFFFFFF)  {  defHandler = pVCExcRec->hander;  pVCExcRec->hander = handler;  break;  }  pVCExcRec = (_EXCEPTION_REGISTRATION *)(pVCExcRec->prev);  } }

где

defHandler – статическая переменная, в которой сохраняется адрес предыдущего обработчика.

handler – наш обработчик исключения.

Разумеется, внимательный читатель уже заметил некоторую нелогичность в этих суждениях. Зачем пытаться зарегистрировать свой обработчик таким изощренным методом, если достаточно поместить свой блок __try __except в функции main? Дело в том, что при использовании MFC, ATL или какой-то иной библиотеки не имеется доступа к пользовательской точке входа, и, стало быть, нельзя установить свой обработчик.

Сейчас пришло время собрать воедино все сказанное выше и написать небольшую программу, иллюстрирующую способ установки обработчика. К статье прилагается файл ehSimple.cpp, в котором вы найдете код установки обработчика. Первый обработчик реализован в виде класса CatUnhandledExceptionFilter, объявленного следующим образом:

class CatUnhandledExceptionFilter { private:  // SEHHandler oldHandler – переменная, в которую будет записан адрес  //предыдущего обработчика исключения. Объявление типа SEHHandler  // было приведено выше.  static SEHHandler oldHandler;    static void zHookUpSEHChain(SEHHandler handler);  static int myHandler(PEXCEPTION_RECORD pEhRecors, PEXCEPTION_REGISTRATION pEhRegRecord, PCONTEXT pContext, void* pp); public:  CatUnhandledExceptionFilter();  ~CatUnhandledExceptionFilter(); };

static void zHookUpSEHChain(SEHHandler handler); – это функция для подмены обработчика исключений runtime-библиотеки. Код ее почти не отличается от предложенного ранее. Единственным изменением является переменная, в которой сохраняется адрес предыдущего обработчика.

static int myHandler(PEXCEPTION_RECORD pEhRecors, PEXCEPTION_REGISTRATION pEhRegRecord, PCONTEXT pContext, PEXCEPTION_RECORD pp); - это наш обработчик, который будет вызван в случае возникновения необработанного исключения.

int CatUnhandledExceptionFilter::myHandler(PEXCEPTION_RECORD pEhRecors,  PEXCEPTION_REGISTRATION pEhRegRecord, PCONTEXT pContext, void* pp) {  printf("*** In My Handler ***\n");  printf("Exception address: 0x%X\n", pEhRecors->ExceptionAddress);  printf("Exception code: 0x%X\n", pEhRecors->ExceptionCode);  return CatUnhandledExceptionFilter::oldHandler(pEhRecors, pEhRegRecord, pContext, pp); }

В программе создается статический объект типа CatUnhandledExceptionFilter. Во время создания этого объекта в конструкторе вызывается функция подмены самого верхнего обработчика исключений. После того, как статические объекты приложения созданы, runtime передает управление функции main, в которой генерируется исключение по доступу к памяти, в результате чего управление переходит нашему обработчику исключения, который сейчас не делает ничего, а просто выводит информацию об исключении на экран и передает управление подмененному обработчику.

После возникновения необработанного исключения операционная система вызывает наш обработчик для обработки исключения и он, выполнив то, что ему нужно, передет управление дальше.

Недостатком приведенного выше кода является то, что он работоспособен только в однопоточных приложениях. Все это происходит потому, что указатель на вершину цепочки EXCEPTION_REGISTRATION находится в структуре TIB, которая хранит в себе информацию о текущем потоке, а значит, при вставке обработчика исключения с использованием значение FS:[0] мы установим обработчик только для одного потока.

Но что же делать в случае многопоточного приложения? Для этого, видимо, нужно проделать описанные действия для каждого потока. К счастью, этого делать не придется. В следующем разделе вы увидите, как обработать необработанные исключения во всех потоках.





Рекомендуемые страницы:

Воспользуйтесь поиском по сайту:
©2015- 2020 megalektsii.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.