イベントログの利用方法

この資料では、メッセージファイルとイベントログを利用するプログラムを作成します。 このサンプルコードによって、イベントログのメッセージが、確かにメッセージファイルから読み込まれていることが確認できるでしょう。

メッセージファイルの作成

はじめにメッセージファイルを作成します。

以下の内容を message.mc として保存します。

;
; メッセージファイル
;

LanguageNames=(English=0x409:MSG00409)
LanguageNames=(Japanese=0x411:MSG00411)

;
;                        カテゴリ
;

MessageIdTypedef=WORD

MessageId=1
SymbolicName=CATEGORY_WINSOCK
Language=Japanese
WinSock系
.

MessageId=2
SymbolicName=CATEGORY_NETWORK
Language=Japanese
ネットワーク系
.

;
;                       メッセージ
;

MessageIdTypedef=DWORD

MessageId=100
SymbolicName=MSG_WINSOCK_INITIALIZE
Language=Japanese
WinSockを初期化しました。
.

MessageId=101
SymbolicName=MSG_WINSOCK_E_INITIALIZE
Language=Japanese
WinSockの初期化に失敗しました。
.

MessageId=102
SymbolicName=MSG_WINSOCK_SOCKETCREATE
Language=Japanese
ソケットを作成しました。
.

MessageId=103
SymbolicName=MSG_WINSOCK_E_SOCKETCREATE
Language=Japanese
ソケットの作成に失敗しました。
.

MessageId=104
SymbolicName=MSG_WINSOCK_UNINITIALIZE
Language=Japanese
WinSockをクリーンアップしました。
.

MessageId=105
SymbolicName=MSG_WINSOCK_E_UNINITIALIZE
Language=Japanese
WinSockをクリーンアップに失敗しました。
.

MessageId=200
SymbolicName=MSG_NETWORK_CONNECT
Language=Japanese
サーバーに接続しました。
IPアドレス=%1
ポート番号=%2
.

MessageId=201
SymbolicName=MSG_NETWORK_E_CONNECT
Language=Japanese
サーバーに接続できませんでした。
IPアドレス=%1
ポート番号=%2
.

MessageId=202
SymbolicName=MSG_NETWORK_RECV
Language=Japanese
データを受信しました。
.

MessageId=203
SymbolicName=MSG_NETWORK_E_RECV
Language=Japanese
データの受信中にエラーが発生しました。
.

MessageId=204
SymbolicName=MSG_NETWORK_SEND
Language=Japanese
データを送信しました。
.

MessageId=205
SymbolicName=MSG_NETWORK_E_SEND
Language=Japanese
データの送信中にエラーが発生しました。
.

次のようにメッセージコンパイラ (mc) でコンパイルします。

> mc message.mc
MC: Compiling message.mc

> nmake

この結果、message.h, message.rc, MSG00411.bin が出力されます。

これを使って DLL を作成します。

次の内容を makefile として保存してください。

CPP=cl.exe
MTL=midl.exe
RSC=rc.exe

TARGETNAME=message
OUTDIR=.\chk

ALL : "$(OUTDIR)\$(TARGETNAME).dll"


CLEAN :
	-@erase "$(OUTDIR)\message.res"
	-@erase "$(OUTDIR)\message.dll"

"$(OUTDIR)" :
    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"

LINK32=link.exe

LINK32_FLAGS=\
	/subsystem:windows\
	/machine:I386\
	/out:"$(OUTDIR)\$(TARGETNAME).dll"\
	/DLL\
	/NOENTRY

LINK32_OBJS= "$(OUTDIR)\message.res"

RSC_PROJ=/l 0x411 /fo"$(OUTDIR)\message.res" /d "_DEBUG"

"$(OUTDIR)\$(TARGETNAME).dll" : "$(OUTDIR)" $(LINK32_OBJS)
    $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS)

.rc{$(OUTDIR)}.res:
	rc.exe $(RSC_PROJ) $<

nmake でビルドします。chk サブディレクトリに message.dll が作られます。 この DLL はリソースのみを含む DLL です。このため、/NOENTRY リンカオプションを指定しています。

テストプログラムの作成

logapp.cpp は次の通り。ここでインクルードした message.h は上で生成されたファイルですから、 テストプログラムのディレクトリにコピーしてください。

#include <winsock2.h>
#include <stdio.h>

#include "debug.h"
#include "message.h"

const char g_szRequest[] = "GET / HTTP/1.1\r\n"
     "Host: localhost\r\n"
     "Connection: close\r\n"
     "\r\n";


char g_szEventSource[] = "EventLogTest_App";
HANDLE g_hEventLog = NULL;


///////////////////////////////////////////////////////////////////////////////


BOOL LogEvent (
     WORD wType,
     WORD wCat,
     DWORD dwEventID,
     WORD wNumStrings,
     DWORD dwDataSize,
     LPCTSTR* lpStrings,
     LPVOID lpRawData  ) {

     assert (g_hEventLog);

     BOOL bRet = ReportEvent ( g_hEventLog, wType, wCat,
          dwEventID, NULL, wNumStrings,dwDataSize, lpStrings, lpRawData);

     if(!bRet) {
          printf("ReportEvent Failed gle = %u.\n", GetLastError());
          return FALSE;
     }

     return TRUE;
}


///////////////////////////////////////////////////////////////////////////////


void main (int argc, char* argv[]) {

     //
     // * Event Log *
     //

     printf ("Registering the event source...");

     g_hEventLog = RegisterEventSource( NULL, g_szEventSource);

     if(NULL == g_hEventLog) {
          printf ("RegisterEventSource Failed. %u\n", GetLastError());
          return;
     }

     printf ("OK\n");

     // 1. Initialize WSA
     WSADATA wsaData;

     int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
     if ( iResult != NO_ERROR ) {
          printf("Error at WSAStartup()\n");
          LogEvent (
               EVENTLOG_ERROR_TYPE,
               CATEGORY_WINSOCK,
               MSG_WINSOCK_E_INITIALIZE,
               0, 0, NULL, NULL);
          return;
     }

     LogEvent (
          EVENTLOG_SUCCESS,
          CATEGORY_WINSOCK,
          MSG_WINSOCK_INITIALIZE,
          0,
          0,
          NULL,
          NULL);

     // 2. Create a socket
     SOCKET hSocket;

     hSocket = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP );

     if ( INVALID_SOCKET == hSocket ) {
          printf( "Error at socket(): %ld\n", WSAGetLastError() );
          WSACleanup();
          LogEvent (
               EVENTLOG_ERROR_TYPE,
               CATEGORY_WINSOCK,
               MSG_WINSOCK_E_SOCKETCREATE,
               0, 0, NULL, NULL);
          return;
     }

     LogEvent (
          EVENTLOG_SUCCESS,
          CATEGORY_WINSOCK,
          MSG_WINSOCK_SOCKETCREATE,
          0, 0, NULL, NULL);

     // 3. Connect to the server
     sockaddr_in saClient;

     saClient.sin_family = AF_INET;
     saClient.sin_addr.s_addr = inet_addr( "127.0.0.1" );
     saClient.sin_port = htons( 80 );

     PCTSTR pConnect[] = { "127.0.0.1", "80" };

     if ( SOCKET_ERROR == connect(
          hSocket, (SOCKADDR*) &saClient, sizeof(saClient) )) {
          printf( "Failed to connect.\n" );
          closesocket (hSocket);
          WSACleanup();
          LogEvent (
               EVENTLOG_ERROR_TYPE,
               CATEGORY_WINSOCK,
               MSG_NETWORK_E_CONNECT,
               2, 0, pConnect, NULL);

          return;
     }

     LogEvent (
          EVENTLOG_SUCCESS,
          CATEGORY_WINSOCK,
          MSG_NETWORK_CONNECT,
          2, 0, pConnect, NULL);

     // 4. Exchange Data
     // 4-1. Send a HTTP request.
     printf ("Sending HTTP Request....");


     INT nLen = 0;
     INT nRet = 0;
     INT nSentBytes = 0;
     INT nTotalBytes = strlen (g_szRequest);
     nLen = nTotalBytes;

     while (nSentBytes < nTotalBytes) {
          nRet = send ( hSocket, g_szRequest, nLen, 0 );
          if ( SOCKET_ERROR == nRet ) { // Error
               printf ("Failed to send.\n");
               break;
          }

          nSentBytes += nRet;
     }

     printf ("Done.\n");

     // 4-2. Receive a HTTP Response.
     printf ("Reading HTTP Response...");

     char szResponse [4 * 1024]; // 4kb for Response buffer.
     char* pBuffer = szResponse;
     char* pEnd = szResponse + (4 * 1024);
     INT nReadBytes = 0;

     while (1) {
          nRet = recv ( hSocket, pBuffer, pEnd - pBuffer, 0);

          if ( 0 == nRet ) { // The connection was gracefully closed.
               printf ("Done. (The connections was gracefully closed.)\n");
               break;
          }
          else if ( SOCKET_ERROR == nRet ) {
               printf ("\n\tFailed to recv (%u)\n", WSAGetLastError());
               break;
          }

          pBuffer += nRet;
          nReadBytes += nRet;

     }

     // 4-3. Display the result.

     PrintHexDump ( nReadBytes, (PBYTE) szResponse);

     // 5. Disconnect
     shutdown (hSocket, SD_SEND);
     shutdown (hSocket, SD_BOTH);

     closesocket (hSocket);

     // 6. Cleanup WinSock
     WSACleanup ();


     //
     // * De-Registering Event Source *
     //

     DeregisterEventSource(g_hEventLog);

}

debug.h は次の通り。

#pragma once

#include <windows.h>
#include <assert.h>

#ifdef DBG
#define ASSERT(x) assert (x)
#else
#define ASSERT(x)
#endif

VOID DebugPrint (LPTSTR   szFormat, ... );
void PrintHexDump (DWORD length, PBYTE buffer);

debug.cpp は次の通り。

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdarg.h>

#include "debug.h"

#define BUFF_SIZE  (511)


///////////////////////////////////////////////////////////////////////////////


VOID DebugPrint (LPTSTR   szFormat, ... ) {

     TCHAR    szBuffer[BUFF_SIZE + 1];
     INT     nWritten;
     va_list args;

     ::ZeroMemory(szBuffer, sizeof(szBuffer));

     // Format error message like printf()
         va_start( args, szFormat );

     nWritten = _vsntprintf( szBuffer, BUFF_SIZE, szFormat, args );

     va_end( args );

     // Output debug string

     ::OutputDebugString( szBuffer );

    }


///////////////////////////////////////////////////////////////////////////////


void PrintHexDump (DWORD length, PBYTE buffer) {

     DWORD i,count,index;
     CHAR rgbDigits[]="0123456789abcdef";
     CHAR rgbLine[100];
     char cbLine;

     for(index = 0; length; length -= count, buffer += count, index += count) {
          count = (length > 16) ? 16:length;

          sprintf(rgbLine, "%4.4x  ",index);
          cbLine = 6;

          for(i=0;i<count;i++) {
               rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4];
               rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
               if(i == 7) {
                    rgbLine[cbLine++] = ':';
               }
               else {
                    rgbLine[cbLine++] = ' ';
               }
          }

          for(; i < 16; i++) {
               rgbLine[cbLine++] = ' ';
               rgbLine[cbLine++] = ' ';
               rgbLine[cbLine++] = ' ';
          }

          rgbLine[cbLine++] = ' ';

          for(i = 0; i < count; i++) {
               if(buffer[i] < 32 || buffer[i] > 126) {
                    rgbLine[cbLine++] = '.';
               }
               else {
                    rgbLine[cbLine++] = buffer[i];
               }
          }

              rgbLine[cbLine++] = 0;

          printf ("%s\n", rgbLine);

     }

} // end PrintHexDump

上記ファイルから nmake します。

> nmake

Microsoft (R) Program Maintenance Utility Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

...

>

ワーニングはたくさん出ますが、好きなように直して使ってください (苦笑)。

ビルドが成功すれば、chk サブディレクトリに logapp.exe が出来上がります。

これを実行します。

> .\chk\logapp

このプログラムは 127.0.0.1 の 80 番ポートに GET リクエストを投げ、 その結果を表示します。ローカルに Web サーバがいれば、HTTP のレスポンスがあるでしょうし、 無ければエラーを返すでしょう。

私のローカルの 80 番ポートには Web サーバがいますので、次のようになりました。

> .\chk\logapp
Registering the event source...OK
Sending HTTP Request....Done.
Reading HTTP Response...Done. (The connections was gracefully closed.)
0000  48 54 54 50 2f 31 2e 31:20 32 30 30 20 4f 4b 0d  HTTP/1.1 200 OK.
0010  0a 44 61 74 65 3a 20 54:75 65 2c 20 32 34 20 46  .Date: Tue, 24 F
0020  65 62 20 32 30 30 39 20:30 34 3a 34 39 3a 32 38  eb 2009 04:49:28
0030  20 47 4d 54 0d 0a 53 65:72 76 65 72 3a 20 41 70   GMT..Server: Ap
0040  61 63 68 65 2f 32 2e 32:2e 38 20 28 57 69 6e 33  ache/2.2.8 (Win3
...

さて、ここでイベントログを確認してください。

以下のように、なにやらわかりにくいメッセージが表示されていると思います。

表示されているメッセージをテキストに落とすと、次のようになります。

Log Name:      Application
Source:        EventLogTest_App
Date:          2/23/2009 8:49:21 PM
Event ID:      201
Task Category: (1)
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      keisukeo-hp
Description:
The description for Event ID 201 from source EventLogTest_App cannot
be found. Either the component that raises this event is not installed
on your local computer or the installation is corrupted. You can install
or repair the component on the local computer.

If the event originated on another computer, the display information had
to be saved with the event.

The following information was included with the event:

127.0.0.1
80

Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="EventLogTest_App" />
    <EventID Qualifiers="0">201</EventID>
    <Level>2</Level>
    <Task>1</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2009-02-24T04:49:21.000Z" />
    <EventRecordID>18093</EventRecordID>
    <Channel>Application</Channel>
    <Computer>keisukeo-hp</Computer>
    <Security />
  </System>
  <EventData>
    <Data>127.0.0.1</Data>
    <Data>80</Data>
  </EventData>
</Event>

このメッセージは何かというと、メッセージファイルがシステムに無いため、 適切なメッセージが表示できない、ということを示しているのです。

メッセージファイルの登録

最初に作成した message.dll を C:\temp にコピーしてください。

次の内容 (レジストリスクリプト) を event-test.reg という名前で保存してください。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\EventLogTest_App]
"TypesSupported"=dword:00000007
"EventMessageFile"="C:\\temp\\message.dll"
"CategoryMessageFile"="C:\\temp\\message.dll"
"CategoryCount"=dword:00000002

event-test.reg をダブルクリックして、レジストリにメッセージファイルの情報を登録します。

さて、メッセージファイルを登録した後、もう一度イベントログに戻ってみてください。

次は以下のように意味のあるメッセージが表示されるはずです。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説