イベントログの利用方法
この資料では、メッセージファイルとイベントログを利用するプログラムを作成します。 このサンプルコードによって、イベントログのメッセージが、確かにメッセージファイルから読み込まれていることが確認できるでしょう。
メッセージファイルの作成
はじめにメッセージファイルを作成します。
以下の内容を 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 をダブルクリックして、レジストリにメッセージファイルの情報を登録します。
さて、メッセージファイルを登録した後、もう一度イベントログに戻ってみてください。
次は以下のように意味のあるメッセージが表示されるはずです。