Преобразование идентификатора сообщения в текст

октября 12 2009 by admin in Администрирование

Вам интересно знать, откуда взять текст для категории события и его подробного описания? С теми знаниями, что у вас уже есть, вы можете решить, что для этого нужно изучить данные источника событий в реестре, загрузить соответствующую DLL и вручную извлечь из ее ресурсов подробное сообщение и категорию... Но ведь это довольно запутано, не так ли? Да, это так, но другого метода получения текста, описывающего событие, нет. К счастью, в системе реализована удобная функция FormatMessage, извлекающая строки из ресурсов, но все остальное зависит от нас. Применение FormatMessage демонстрируется в приложении EventMonitor, приведенном в конце главы. Эта функция определена так:
DWORD FormatMessage( DWORD dwFlags, PCVOID pSource, DWORD dwMessageld, DWORD dwLanguageld, PTSTR pBuffer, DWORD nSize, va_list «Arguments);
Мне же больше бы понравились такие функции:
PTSTR GetEventCategory( PTSTR pszLog, PEVENTLOGRECORD pelr);
PTSTR GetEventMessage( PTSTR pszLog, PEVENTLOGRECORD pelr);
Они получали бы указатель на регистрационный файл и запись о событии, а возвращали буфер, содержащий запрашиваемый текст. Этот буфер можно было бы освободить с помощью LocalFree, как это делает FormatMessage. Но в системе такие замечательные функции не реализованы. Поэтому я сам создал их в виде макроса и функции-оболочки для моей функции FormatEventMessage. Полный код последней можно найти в файле EventMonitor.cpp на прилагаемом компакт-диске. Теперь перейдем к главному.
Вы, вероятно, помните, что DLL-файлы сообщений указываются в значениях реестра EventMessageFile, ParameterMessageFile или CategoryMessageFile в зависимости от искомого сообщения. Для источника событий MySource, события которого регистрируются в файле Application, реестр будет выглядеть так:
HKEY_LOCAL_MACHINE SYSTEM
CurrentControlSet Services EventLog
Application MySource
CategoryMessageFile
EventMessageFile
ParameterMessageFile
В обязанности вашего приложения входит поиск нужного значения реестра и анализ хранящейся в нем информации об одной или нескольких DLL, содержащих сообщения. Не забывайте, что полученную строку, хранящую разделенные точкой с запятой названия файлов сообщений, перед ее использованием для загрузки модулей в адресное пространство нужно передать в Expand-EnvironmentStrings, так как тип всех вышеописанных значений реестра — REG_EXPAND_SZ.
Получив список DLL- и ЕХЕ-файлов сообщений, вы можете, используя Load-LibratyEx, загрузить их по порядку (слева направо) в адресное пространство вашего процесса. Применяйте для этого именно LoadLibraryEx, а не LoadLibrary, так как она, благодаря флагу LOAD_LIBRARY_AS_DATAFILE, позволяет загрузить модуль, содержащий только ресурсы. Полученный от LoadLibraryEx описатель вместе с идентификатором сообщения и подстановочными строками нужно передать из структуры EVENTLOGRECORD в FormatMessage. При успешном выполнении этой функции ваше сообщение будет найдено, и вы сможете выгрузить DLL-файл сообщений и вернуть текст. Если вызов функции будет неудачным, придется выгрузить DLL, загрузить другой файл и попробовать найти сообщение в нем. Если понадобится извлекать сообщения для нескольких событий, можете оптимизировать код с целью предотвращения постоянных загрузок и выгрузок DLL-файлов.
Хотя FormatMessage автоматически подставляет в сообщение преданные вами строки, текст из библиотек DLL, указанных в значении реестра ParameterMessageFile. в этом процессе не участвует. Вам нужно вручную проверять наличие символов <%%» в строке, возвращаемой FormatMessage, и, используя вышеописанный алгоритм, заменять их соответствующим текстом. Все эти действия продемонстрированы в коде приложения EventMonitor.
И еще одно замечание о FormatMessage. Если в текст сообщения нужно подставить больше строк, чем было передано в FormatMessage, функция слепо попытается получить доступ к несуществующим строкам. Скорее всего это вызовет нарушение доступа. Следовательно, вы должны заранее проанализировать сообщение и подсчитать требуемое количество подставляемых строк или — и это намного проще — поместить вызов FormatMessage в блок обработки ошибок, который позволит обойти нарушение доступа. Помните: система ничего не гарантирует относительно правильности сообщаемых приложениями событий.
Раз мы обсуждаем надежность просмотра событий, я должен сказать, что источник событий может указывать на ошибочный или несуществующий файл сообщений. Ram код должен уметь обрабатывать и такие ситуации.
BackupEventLog создает файл с переданным ей названием, копирует в него содержимое регистрационного файла, определенного в параметре hEventLog. Эта функция позволяет администратору сохранять хронологию событий.
Чтобы прочитать файл хронологии событий, приложение должно вызвать функцию OpenBackupEventLog, возвращающую описатель регистрационного файла (подобно описанной выше OpenEventLog). Используя этот описатель, можно вызывать уже знакомые вам функции, считывающие записи о событиях. По окончании работы с регистрационным файлом, нужно закрыть его описатель с помощью CloseEventLog. Резервирование регистрационных файлов можно использовать для архивирования и последующего изучения событий, кроме того, этот процесс упрощает задачу клиентов по подготовке и пересылке вам файлов с целью получения совета по поводу возникших неисправностей.
Последняя функция, ClearEventLog, просто удаляет все записи о событиях из файла, открытого с помощью OpenEventLog или OpenBackupEventLog. Для удобства она позволяет создать резервную копию файла перед удалением его записей. Чтобы очистить регистрационный файл без создания его резервной копии, в параметр pszBackupFileName нужно передать NULL.