Приложение может записывать сообщения в журнал используя следующие функции
WinAPI. Подробное описание параметров этих функций содержится в документации к
API. RegisterEventSource - Открывает handle для доступа к журналу на
локальной или удаленной машине. ReportEvent - Собственно записывает
сообщение. Для записи сообщений в журнал в упрощенной манере просто произведите
вызов RegisterEventSource с именем машины (UNC), в журнал которой вы
хотите поместить сообщение (nil для локальной машины), и именем события. Имя
события это обычно имя приложения, но может быть чем-то более информативным. Как
только источник событий зарегистрирован, можно записывать события при помощи
ReportEvent с handle, который вернула
RegisterEventSource.
Пример:
VAR EventLog:THandle;
EventLog:=RegisterEventSource(nil,PChar('MyApplication'));
VAR MyMsg:Array[0..2] of PChar;
MyMsg[0]:='A test event message';
ReportEvent(EventLog,EVENTLOG_INFORMATION_TYPE,0,0,nil,1,0,@MyMsg,nil);
Однако текст сообщения,
записанного в журнал будет предварен текстом: "The
description for Event ID ( 0 ) in Source ( MyApplication ) cannot be found. The
local computer may not have necessary registry information or message DLL files
to display messages from a remote computer. The following information is part of
the event:" (Не найдено описание для события
с кодом ( 0 ) в источнике ( MyApplication ). Возможно, на локальном компьютере
нет нужных данных в реестре или файлов DLL сообщений для отображения сообщений
удаленного компьютера. В записи события содержится следующая информация:)
(Замечание: Это сообщение специфично для Windows2000 и может немного отличаться
на других версиях). Для предотвращения появления этого текста
необходимо внести в реестр некоторые ключи, как показано ниже, и определить
строковые ресурсы (это может быть выполнено любым компонентом вашего приложения,
не обязательно приложением, которое будет записывать события). Соответствующие
записи реестра описаны ниже. Примеры кода предполагают, что строковые ресурсы и
категории расположены в том же исполняемом файле, который содержит программу,
записывающую события. Ключи категорий являются опциональными. Смысл
этих ключей реестра и строковых ресурсов в том, что журнал событий использует
строку, а приложение записывает в журнал в виде форматированного аргумента, и
журналу необходимо знать, где находится описатель формата для этой строки. Кроме
того, в журнале может храниться информация о категории события, полезная для
просмотра событий. Это удобнее, чем просто отображать множество однотипный
событий "Нет". Самый простой определитель формата это %1, который просто
передаст в журнал входную строку. Для более подробного изучения определителей
формата см. API документацию для FormatMessage.
Ключи реестра
Создайте
следующий ключ реестра:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\<App
Name> Имя приложения AppName должно совпадать с именем источника,
использованного при вызове RegisterEventSource, потому что просмотрщик
событий будет использовать это имя для отыскивания событий. Создайте
следующие ключи:
Имя ключа
|
Тип
|
Описание
|
CategoryCount
(Optional)
|
Integer
|
Количество категорий
событий, которые вы собираетесь использовать.
(Это максимальная величина, и не будет проблем,
если не все категории на самом деле будут
применяться).
|
CategoryMessageFile
(Optional)
|
String
|
Файл, содержащий ресурсы
строк категорий.
|
EventMessageFile
|
String
|
Файл, содержащий ресурсы
строк событий.
|
TypesSupported
|
Integer
|
Допустимые типы событий.
|
Пример кода для создания необходимых записей в
реестре:
VAR
Reg:TRegistry;
RegKey:String;
AppPath:String;
AppName:String;
NumCategories:Integer;
Begin
Reg:=TRegistry.Create;
Try
AppPath:=Application.ExeName;
AppName:=’MyApplication’;
NumCategories:=2;
RegKey:=
Format(‘SYSTEM\CurrentControlSet\Services\EventLog\Application\%s’,[AppName]);
Reg.RootKey:=HKEY_LOCAL_MACHINE;
Reg.OpenKey(RegKey,True);
Reg.WriteString(‘CategoryMessageFile’,AppPath); // Собственное имя
Reg.WriteString(‘EventMessageFile’,AppPath); // Собственное имя
Reg.WriteInteger(‘CategoryCount’,NumCategories); // Максимальное количество категорий
Reg.WriteInteger(‘TypesSupported’,EVENTLOG_SUCCESS or EVENTLOG_ERROR_TYPE or
EVENTLOG_WARNING_TYPE or EVENTLOG_INFORMATION_TYPE); // Разрешаем все типы
Reg.CloseKey;
EventLog:=RegisterEventSource(nil,PChar(AppName));
Finally
Reg.Free;
End; //try..finally
End;
Сообщение и
ресурсы категорий.
Информация, помещаемая в реестр
вышеприведенным кодом, информирует журнал событий о том, где искать строки
событий и категорий, основываясь на имени источника, которое использует
приложение для записи в журнал. И, как мы уже говорили, чтобы журнал событий
искал эти строки в нашем исполняемом файле, нам нужно включить эти строковые
ресурсы в наш файл. Этот процесс состоит из следующих шагов:
- Написание исходного файла таблицы сообщений (файл .mc).
- Компиляция .mc файла при помощи Microsoft message compiler.
- Подключение получившейся информации к нашему Delphi приложению.
Есть много примеров по написанию .mc
файлов в Windows SDK и на различных сайтах,
включая MSDN, однако документация не достаточно
проста, поэтому приводим минимально достаточное описание для создания файла
таблицы сообщений:
;//Example Message source file exmess.mc
MessageId=0
Language=English
%1
MessageId=1
Language=English
Category1
MessageId=2
Language=English
Category2
Строки, начинающиеся с ;//
являются комментариями и не компилируются. Этот пример содержит три строковых
ресурса - один определитель формата сообщения и две категории, хотя файл может
содержать только первый ресурс. Каждый ресурс отделен одной отдельной точкой на
строке, так же, как и в конце файла. Если в конце файла отсутствует перевод
строки после точки, то файл не будет скомпилирован. Первая строка
каждого ресурса является MessageID (index),
при помощи которого приложение будет обращаться к строке. Следующая строка
указывает язык ресурса. В нашем случае "English” –
означает international English, язык по умолчанию для
всех Windows платформ. Информацию по многоязыковым
ресурсам см. в справке к компилятору ресурсов. Последняя строка определяет
собственно текст сообщения. В случае ресурса 0, строка будет "%1”,
что означает, что передается сама строка. Если, например, нужен префикс
сообщения "An Event Message” (Сообщение события), то
строка будет иметь вид: "An Event Message %1”. Более
полное описание форматов см. в API справке по
FormatMessage и компилятору ресурсов.
Ресурсы категорий не требуют форматированных аргументов. Как видно в примере, мы
определили две категории "Category1” и "Category2”.
Следующий этап – компиляция .mc файла при помощи
Microsoft message compiler (mc.exe),
который можно взять у Microsoft (входит в состав
Platform SDK). Наш пример, имеющий имя "exmess.mc”
может быть скомпилирован из командной строки таким образом:
Mc exmess.mc
В результате получаем три
файла: exmess.rc,
bin00001.msg и
exmess.h.
emess.h может быть использован как заголовочный
файл для обращения к ресурсам по их символическим именам, если таковые указаны
(в нашем примере нет). .bin файл это откомпилированный
бинарный ресурс с сообщениями, .rc это файл ресурсов Windows. Он может быть откомпилирован в
Delphi .res файл при помощи
brcc32.exe – компилятора
ресурсов Delphi или просто добавлен в проект при
помощи project manager, и тогда
Delphi автоматически его откомпилирует при компиляции проекта (build).
Запись событий
с категориями.
Теперь наше приложение имеет ресурсы и необходимые записи в реестре или код, который их
внесет. Значит, приложение может записывать события в журнал без сообщения об
отсутствии ресурсов и с добавочным индексом категории события:
VAR EventLog: THandle;
EventLog:=RegisterEventSource(nil,PChar(‘MyApplication’));
VAR MyMsg:Array[0..2] of PChar;
MyMsg[0]:=’A test event message’;
ReportEvent(EventLog, EVENTLOG_INFORMATION_TYPE,1,0,nil,1,0,@MyMsg,nil);
Вышеприведенный код запишет
событие в журнал с текстом "A test event message” и,
потому что 1 следует за параметром EventLogType, это
будет событие категории "Category1”. Это
достигнуто указанием 0 в качестве идентификатора события, который соответствует
определителю формата в ресурсе 0 ("%1”). В результате текст сообщения события
будет передан без изменения. Точно так же, категория указана 1, что
соответствует "Category1” в нашем ресурсе 1.
Журнал событий поддерживает "живую связь” с файлами сообщений и категорий,
указанных в реестре, что означает, что когда пользователь захочет просмотреть
журнал, просмотрщик событий получит доступ к файлам ресурсов для детального
отображения событий. Это также означает, что если вы создадите множество
событий, при помощи указанного файла ресурсов, и, затем, измените значения в
файле ресурсов и произведете обновление (refresh) в
просмотрщике событий, тексты событий и номера категорий так же изменятся в
соответствии с ресурсами. Точно так же, если файл ресурсов вдруг будет удален
или записи в реестре будут уничтожены или повреждены, то журнал не сможет
получить доступ к ресурсам, и отобразит сообщение с ошибкой в виде префикса
события, как было описано в начале статьи. В этом случае вместо номера категории
события будет отображен индекс категории.
|