Как стать автором
Обновить

Регистрация событий С++ приложения в системном журнале Windows

При разработке любого приложения (ПО) встает необходимость регистрации событий (логиррования). Ниже я расскажу как регистрировать события в системном журнале Windows из C++ приложения и как максимально удобно настроить Visual Studiuo 2010 для использования в данном контексте.

Содержание:
	1. Введение:
		1.1. Легенда
	2. Разработка программы:
		2.1. Создание файла сообщений;
		2.2. Подключение ресурсов и их использование;
	3. Внедрение программы;
	4. Ссылки по теме.
	


1. Введение


Регистрация событий в Windows не так проста и прозрачна как например ведение лога при помощи библиотеки boost.log. Но использование журнала событий в качестве лога, иногда выгоднее т.к. не нужно думать о:
1. размере лог-файла (если быть честным то с boost.log тоже об этом не нужно думать);
2. совместимости форматов логов;
3. проблемах возникающих при одновременном просмотре лога и его пополнении.

1.1. Легенда

Для примера рассмотрим ситуацию из моей практики. У нас есть ПО, которое через COM-порт управляет устройством включения — железкой с выходами питания 220В. ПО может подавать или отключать питание на том или ином порту железки и должно логгировать исполнение команд.

2. Разработка программы


2.1. Создание файла сообщений

Первое, что необходимо сделать это определится с категориями событий и самими событиями. В нашем случае существует две категории событий:
1. События связи;
2. События команд.

И существует четыре события:
1. Установка связи с устройством — информационное событие;
2. Потеря связи с устройством — событие-ошибка;
3. Выполнение включения выхода — информационное событие или событие-ошибка;
4. Выполнение выключения выхода — информационное событие или событие-ошибка.

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

Щелкаем “Проект” > “Добавить новый элемент”, в правой панели “Установленные шаблоны” выбираем “Служебная программа”, в средней панели выбираем “Текстовый файл (.txt)”, вводим название файла “messages.mc”. В файле пишем (описание формата).

; // язык сообщений
LanguageNames=(Russian=0x419:MSG00419)

; // определение категорий сообщений

MessageIdTypedef=WORD

MessageId=0x1
SymbolicName=CONN_CATEGORY
Language=Russian
События связи
.

MessageId=0x2
SymbolicName=CMD_CATEGORY
Language=Russian
События команд
.

; // определения сообщений

MessageIdTypedef=DWORD

MessageId=0x100
SymbolicName=MSG_CONNECTION_ESTABLISHED
Language=Russian
Установлена связь с устройством.
.

MessageId=0x101
SymbolicName=MSG_CONNECTION_ESTABLISHED
Language=Russian
Связь с устройством потеряна.
.

MessageId=0x102
SymbolicName=MSG_POWER_ON_SUCCSESS
Language=Russian
Выполнено включения выхода.
.

MessageId=0x103
Severity=Error
SymbolicName=MSG_POWER_ON_FAILED
Language=Russian
Включение выхода не выполнено.
.

MessageId=0x104
SymbolicName=MSG_POWER_OFF_SUCCSESS
Language=Russian
Выполнено выключение выхода.
.

MessageId=0x105
Severity=Error
SymbolicName=MSG_POWER_OFF_FAILED
Language=Russian
Выключение выхода не выполнено.
.


Полученный файл компилируется компилятором сообщений (mc.exe) в файл ресурсов. Сделать это можно из командной строки Visual Studio (Пуск > Все программы > Microsoft Visual Studio 2010 > Visual Studio Tools > Коммандная строка Visual Studio) выполнив команду mc -U messages.mc предварительно перейдя в каталог проекта.

Но лучше настроить, чтобы файл компилировался автоматически во время сборки проекта. Для этого щелкаем правой кнопкой на файле messages.mc и выбираем “Свойства”, в комбобоксе “Конфигурация” выбираем “Все конфигурации”, в правой панели для “Типа элемента” выбираем “Настраиваемый инструмент построения” и жмем “Применить”. В левой панели появилась “папка” “Настраиваемый инструмент построения” — жмем на нее, проверяем что в верхнем комбобоксе “Конфигурация” выбрано “Все конфигурации” и в правой панели указываем следующие параметры:
Коммандная строка: mc "%(FullPath)"
Описание: Compiling Messages...
Выводы: %(Filename).rc;%(Filename).h;MSG00419.bin

Жмем “Применить” и закрываем окно.

Теперь можем открыть наш файл messages.mc и выбрать “Построение” > “Компиляция” — мы получим файлы messages.rc, messages.h, MSG00419.bin.

2.2. Подключение ресурсов и их использование

Теперь нам нужно добавить в проект файл ресурсов если его еще нет. “Проект” > “Добавить новый элемент”, в правой панели “Установленные шаблоны” выбираем “Ресурс”, в средней панели выбираем “Файл ресурсов (.rc)”, вводим название файла “resources.rc”. Открываем “Окно ресурсов”, разворачиваем наш проект и щелкаем на “resources.rc” правой кнопкой и выбираем “Включение ресурсов”. В появившемся окне в поле “Директивы времени компиляции” вводим #include «messages.rc»

Ресурсы созданы и подключены — тепер можно писать код. Нужно подключить сгенерированный messages.h и смело использовать определенные ранее категории сообщений. Например можно написать:

	#include <windows.h>
	#include "messages.h"

	…

	void Logger::init() {
	    h_event_log_ = RegisterEventSource(NULL, L“MyEventProvider”);
	}

	…

	void Logger::logPowerOnSuccess(LPWSTR message, LPWSTR data) {
	    LPWSTR pMessages[1] = {message};
	    DWORD dwEventDataSize = ((DWORD)wcslen(data) + 1) * sizeof(WCHAR);

	    if (!ReportEvent(
	                            h_event_log_,
	                            EVENTLOG_INFORMATION_TYPE,
	                            CMD_CATEGORY,
	                            MSG_POWER_ON_SUCCSESS,
	                            NULL,
	                            1,
	                            dwEventDataSize,
	                            (LPCWSTR*)pMessages, data)
	    ) {
	        /* тут нужно обработать ошибку, но лучше не так 
	        wprintf(
	                L"ReportEvent failed with 0x%x for event 0x%x.\n",
	                GetLastError(),
	                MSG_APPLICATION_START
	        );
	        */
	    }
	}
	


Кстати если мы используем библиотеку boost.asio для работы по COM-порту включать файл windows.h нужно после включения заголовков boost’а.

3. Внедрение программы


После компиляции кода и запуска программы зайдя в журнал и выбрав ваше событие вы скорее всего увидите что-то похожее:

“Не удается найти описание для идентификатора события… из источника… Вызывающий данное событие компонент не установлен на этом локальном компьютере или поврежден. Установите или восстановите компонент на локальном компьютере.

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

К событию были добавлены следующие сведения:… “


Чтобы этого не появлялось на машине, на которой будет выполняться ваша программа вам нужно создать DLL. Зайдите в командную строку Visual Studio (Пуск > Все программы > Microsoft Visual Studio 2010 > Visual Studio Tools > Коммандная строка Visual Studio), перейдите в папку проекта содержащую resources.res (например папка Debug) и выполните

link /DLL /NOENTRY resources.res

Вы получите файл resources.dll в той же папке.

При внедрении программы необходимо будет зарегистрировать resources.dll в реестре. Создайте ключ MyEventProvider (см. функцию Logger::init()) в HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application и установите следующие параметры:
CategoryCount REG_DWORD 0x00000002
CategoryMessageFile REG_SZ path\resources.dll
EventMessageFile REG_SZ path\resources.dll
ParameterMessageFile REG_SZ path\resources.dll
TypesSupported REG_DWORD 0x00000006

Где CategoryCount — количество категорий сообщений, CategoryMessageFile, EventMessageFile, ParameterMessageFile — путь до вашей DLL, а TypesSupported — количество типов сообщений.

4. Ссылки по теме:


Формат файлов сообщений: Message Text Files
Описание функций: RegisterEventSource, ReportEvent
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.