Смекни!
smekni.com

Централизованная обработка исключений (стр. 3 из 3)

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

Если вы согласны с перечисленными недостатками использования функции SetUnhandledExceptionFilter, то давайте подумаем, как получить управление во время возникновения необработанного исключения. Если вы не согласны, то можете пропустить следующий раздел и перейти непосредственно к разделу «Сбор и сохранение информации о состоянии процесса».

Получение управления

До сих пор речь шла о механизме обработки исключений, используемом в ОС Windows, и о том, как он используется компиляторами. Реализованный выше простой алгоритм подменяет установленный компилятором обработчик, что позволяет получить управление при возникновении в приложении необработанного исключения. Но алгоритм был рассчитан на однопоточное приложение, а это значительно сужает возможности его использования. Кроме всего прочего, если в приложении возникает необработанное исключение, то установленный Runtime-библиотекой обработчик вызывает функцию UnhandledExceptionFilter, которая также может вызваться операционной системой. Таким образом, функция UnhandledExceptionFilter является ключевой системной функцией, которая обрабатывает все необработанные исключения. Подмена этой функции позволила бы выполнить поставленную задачу.

Поскольку функция UnhandledExceptionFilter может быть вызвана как из обработчика Runtime, так и системой, то изменение ее адреса в таблице импортируемых функций может не решить всей проблемы, поэтому для большей надежности нужно изменить код функции UnhandledExceptionFilter так, что бы сразу после ее вызова управление попадало к нам. К статье прилагается проект ehfilter, в котором реализован класс CatUnhandledExceptionFilter, устанавливающий обработчик необработанных исключений. Объявление класса CatUnhandledExceptionFilter приведено ниже:

class CatUnhandledExceptionFilter{private: friend LONG myUnhandledExceptionFilter( _EXCEPTION_POINTERS* ExceptionInfo ); UINT_PTR m_oldSystemUnhandledFilter; LONG UnhandledExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo);public: CatUnhandledExceptionFilter(); ~CatUnhandledExceptionFilter(); ool HookUpUnhandledFilter();};

где

m_oldSystemUnhandledFilter – адрес оригинальной функции UnhandledExceptionFilter.

myUnhandledExceptionFilter – дружественная классу функция-переходник, ее назначение и код будут рассмотрены ниже.

UnhandledExceptionFilter – наш фильтр необработанных исключений

HookUpUnhandledFilter – функция установки нашего фильтра исключений.

Проект ehfilter является обычной DLL, которая должна быть загружена в адресное пространство приложения. Во время загрузки библиотеки в файле main.cpp создается глобальная переменная gFeedBackFilter типа CatUnhandledExceptionFilter. Во время создания этой переменной в конструкторе определяется адрес функции UnhandledExceptionFilter и запоминается в переменной m_oldSystemUnhandledFilter. Когда в библиотеку приходит сообщение DLL_PROCESS_ATTACH, вызывается функция HookUpUnhandledFilter, которая устанавливает наш фильтр необработанных исключений.

Код функции HookUpUnhandledFilter приведен ниже:

bool CatUnhandledExceptionFilter::HookUpUnhandledFilter(){ if ( m_oldSystemUnhandledFilter == 0 ) return false; DWORD addr = m_oldSystemUnhandledFilter; DWORD old = 0; if ( TRUE == VirtualProtect((LPVOID)addr, 5, PAGE_READWRITE, &old) ) { unsigned char *p = (unsigned char*)addr; *p = 0xE9; UINT_PTR ehFilter = (UINT_PTR)myUnhandledExceptionFilter; addr += 5; ehFilter = ehFilter - addr; p++; DWORD *pp = (DWORD*)p; *pp = ehFilter; m_oldSystemUnhandledFilter += 5; VirtualProtect((LPVOID)addr, 5, old, &old);return true; } return false;}
ПРИМЕЧАНИЕЭтот код будет работать только в семействе ОС Windows NT. Дело в том, что 2 верхних гигабайта, где размещены системные библиотеки, в Windows 9х недоступны на запись из пользовательского (user) режима. – прим.ред.

Сначала функция проверяет, был ли найден системный обработчик UnhandledExceptionFilter. Если он не найден, функция возвращает false и завершает свою работу. Затем, поскольку необходимо писать в системную область памяти, изменяются атрибуты доступа к ней, что делает ее доступной для чтений/записи, и записывается инструкция безусловного перехода на функцию-переходник myUnhandledExceptionFilter. Функция-переходник имеет две цели – это вызвать фильтр и вернуть управление системной функции.

Приведенные в примере реализации функций HookUpUnhandledFilter и myUnhandledExceptionFilter являются сильно упрощенными и, конечно, неприменимы в реальной жизни. Мало того, попытка их применения может привести к печальным последствиям. Однако они достаточны для иллюстрации механизма подмены вызова системных функций. В реальности же необходимо дизассемблировать код функции UnhandledExceptionFilter, запоминать его и затем использовать при возврате управления. Но это в значительной мере усложнило бы код и могло скрыть основной его смысл, поэтому я решил оставить эту реализацию для демонстрации самого факта возможности подобных действий.

Сбор и сохранение информации о состоянии процесса

Как было упомянуто выше, функция UnhandledExceptionFilter принимает один аргумент, являющийся указателем на _EXCEPTION_POINTERS. В этой структуре сохранена информация о состоянии приложения после возникновения исключения, состояние регистров, информация об исключении. Но этого может быть мало для восстановления реальной картины произошедшего. Поэтому в своем обработчике я попытаюсь собрать ту информацию, которая поможет понять причину сбоя. Объем этой информации может зависеть от типа приложения и нельзя привести весь список необходимых параметров, способных помочь при анализе причины падения приложения. Программист должен сам решить, какой объем и тип информации ему требуется. Здесь я хочу лишь сказать, что информации, которую собирает о приложении библиотека dbghelp.dll, поставляемая с Windows, достаточно для большинства приложений. dbghelp.dll сохраняет следующее:

- информация о потоках;

- информация о загруженных модулях;

- информация об исключении;

- информация о системе;

- информация о памяти.

После сбора информации ее можно сохранить в файле на диске для последующего анализа и разбора.

Заключение

Операционная система Windows предоставляет каждому разработчику уникальную возможность обрабатывать исключения, возникающие в его программах, с помощью механизма Структурированной Обработки Исключений (SEH). Но приложения не полностью используют возможности, предоставляемые этим системным сервисом, и оставляют часть ошибок необработанными. В статье я привел пример того, как получить управление в случае фатальной ошибки приложения, и дал приложению получить управление вместо того, чтобы быть просто выгруженным. Этот метод может дать приложению последний шанс:

сохранить данные;

собрать информацию о состоянии на момент ошибки;

хотя бы извиниться....