2010年1月18日月曜日

[WIN]イベントビューアのログにメッセージを出す。

イベントビューアにログを吐き出すだけなら、ReportEvent()を使えば出来るのですが、ただ何もせずにReportEvent()でメッセージを吐き出すと、イベントビューア上では下記のように言われてしまいます。

「イベント ID (0) (ソース ***内) に関する説明が見つかりませんでした。
リモート コンピュータからメッセージを表示するために必要なレジストリ情報またはメッセージ DLL ファイルがローカル コンピュータにない可能性があります。」

イベントビューアにMS製のやつみたいにちゃんとしたメッセージを出すには、次のような手順が必要です。

1)メッセージリソースを作る。
イベントログのメッセージは、イベントログがメッセージの実体を持っているわけではなく、レジストリが示しているリソース(EXE, DLL)から取得します。
その、メッセージリソースをまず作ります。
下記のような内容を、message.mc等のファイル名で保存し、コンパイルします。
コンパイルは、mc message.mc 等のように入力して行い、その結果 MSG00411.binとmessage.rc、message.hが生成されます。

    MessageIdTypedef=DWORD
    LanguageNames=(Japanese=0x411:MSG00411)

    MessageId=0x1
    SymbolicName=MSG_ERR_LOGFILEERR
    Language=Japanese
    ログファイルへの書き込み操作中にエラーが発生しました。
    .

    MessageId=0x2
    SymbolicName=MSG_ERR_LOGFILEOPENERR
    Language=Japanese
    ログファイルが開けません。
    .

2)メッセージリソースをリソースファイルに取り込む。
先に出来た、message.rcをアプリケーションのリソースファイルにインクルードします。
MFCベースなら、*.rc2に#include "message.rc"とするのが、よいでしょう。

3)プログラムを書く
下記のような、プログラムを作ります。ここで、InitEventLog()はアプリケーション起動時に1回だけ呼び出し、レジストリに登録します。
ReportEventLog()でイベントログを記録します。その際、dwIDEventにmessage.hにあるメッセージIDを指定することにより、イベントビューアで見たときにメッセージリソースのメッセージが表示されるようになります。

void InitEventLog()
{
    HKEY hk; 

    // レジストリキーの生成
    // MyApplicationNameは自分のアプリ名にすること(ReportEventLog()と同じアプリ名)
    RegCreateKey(HKEY_LOCAL_MACHINE,
        (LPCTSTR)"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MyApplicationName",
        &hk);

    // メッセージリソースを入れたアプリのパスを取得(普通、自分自身)
    char szPath[_MAX_PATH];
    GetModuleFileName(AfxGetInstanceHandle(), szPath, _MAX_PATH);

    // イベントIDメッセージファイルをサブキーに登録する
    RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, 
        (LPBYTE)szPath,           // ここにメッセージリソースを入れたアプリのパスを登録
        strlen(szPath) + 1);
 
    // イベントログのサポートタイプを登録
    DWORD dwData; 
    dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | 
             EVENTLOG_INFORMATION_TYPE; 
 
    RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD,
        (LPBYTE) &dwData, sizeof(DWORD));
 
    RegCloseKey(hk); 
}

void ReportEventLog(WORD nType, WORD nCategory, DWORD dwIDEvent, LPVOID lpData, DWORD dwLen)
{
    HANDLE h = RegisterEventSource(NULL, AfxGetAppName());

    if (h != NULL)
    {
        ReportEvent(h, nType, nCategory, dwIDEvent, NULL, 0, dwLen, NULL, lpData);
        DeregisterEventSource(h); 
    }
}


【注意事項】ここに掲載されている内容について引用流用は自由ですが、内容やサンプルに基づくいかなる結果に関して一切の責任を負いません。自己の責任の上でご活用ください。