| |||||||||||||||||
|
|||||||||||||||||
Windows関連 Cryptography API: Next Generationを使う Solaris関連 画像入出力 その他アルゴリズムなど |
イベントログにメッセージを出力する2008年5月25日公開 1.はじめにWindowsにはイベントログという、ログを記録・管理する機能がある。これは、システムやアプリケーションで発生したイベントを記録し、ある程度メッセージがたまったら、容量や期間などの設定に合わせて、適宜不要なメッセージを削除してくれるというような機能である。 また当然だが、この機能はWindowsの本体だけが利用できる機能というわけではなく、Windows上で動作する一般のアプリケーションからでも利用できるようにAPIが公開されている。 一般的にいって、アプリケーションからしてみればログ出力は本質的な処理ではない。ましてや、ログファイルのメンテナンスや世代管理などは、工数がかかるくせして目的達成には何の貢献もないという、極めて厄介なお荷物的存在である。というか、荷物以外の何物でもない。であれば、使える物は積極的に活用して、どうでもいい部分については手を抜くのが賢いやり口というものだろう。 だが、イベントログへのメッセージ出力は、単にAPIを叩けばいいというだけのものではなく、いくらかの知識と手数が必要になる。このページでは、イベントログへメッセージを出力する方法について解説したいと思う。 2.メッセージの出力基本的には、下記の処理でイベントログにメッセージを出力してやることができる。 const char *pMsg = "Error Message"; HANDLE h = RegisterEventSource( NULL, "myapp" ); ReportEvent( h, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 1, 0, &pMsg, NULL ); DeregisterEventSource( h ); 2.1 RegisterEventSource関数イベントログにアクセスするためのハンドルを返す。 プロトタイプ HANDLE RegisterEventSource( LPCTSTR lpUNCServerName, LPCTSTR lpSourceName ); lpUNCServerName イベントログを出力する先のサーバ名を指定する。ローカルのイベントログに書き込みたければNULLを指定すればいい。 lpSourceName これがくせ者で、いろいろと考えなければいけない。とりあえずここでは、適当なアプリケーションの名前を指定しておくものとする。 戻り値 イベントログに書き込むためのハンドルが返される。失敗した場合はNULLが返される。 2.2 ReportEvent関数イベントログにメッセージを書き込む。 プロトタイプ BOOL ReportEvent( HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, PSID lpUserSid, WORD wNumStrings, DWORD dwDataSize, LPCTSTR *lpStrings, LPVOID lpRawData ); hEventLog RegisterEventSource関数で取得したハンドルを指定する。 wType 以下の値を指定する。
出力するメッセージにあわせて、好きな奴を適当に選択すればいい。 wCategory イベントログのシステム的には使われない。アプリケーションの都合で勝手な値を指定すればいいらしい。 dwEventID イベントのIDを指定する。 基本的にはアプリケーションの都合で任意の番号を出力させることができるのだが、後でいろいろとこの番号について考えなければいけないことがあるので、注意が必要である。 なお、この番号は必ず正の値でなければいけない。 lpUserSid セキュリティ識別子を指定する。特に意志がないのならNULLを指定しておけばいい。 wNumStrings lpStringsに指定する文字列の個数(文字数では無い)を指定する。 dwDataSize lpRawDataに指定するデータ長を指定する。 lpStrings 出力するメッセージの、文字列の配列(const char**)を指定する。各文字列は最大32KBでNULLで終わっていなければならない。この引数の配列長(文字列の個数)は、wNumStringsに指定する。 lpRawData メッセージに付加して出力するバイナリデータを指定する。データ長はdwDataSizeに指定する。 戻り値 成功した場合は真が返される。 2.3 DeregisterEventSource関数RegisterEventSourceで取得したイベントログに書き込むためのハンドルを閉じる。 プロトタイプ BOOL DeregisterEventSource( HANDLE hEventLog ); hEventLog ハンドルを指定する。 戻り値 成功した場合は真が返される。
3 出力内容上記のプログラムを実行すると、下図のようなメッセージが出力される。 Windows NT 4.0 Windows XP "Error Message"という自分で出力しようとしたメッセージ以外にも、なんかよくわからない余分なメッセージが一緒に出力されている。 気にしないのならそれでいいのかもしれないが、しかし、こんな変なものが表示されてしまうのでは、アプリケーションソフトとしてはなはだ不都合である。だから、次はこのメッセージの意味と消し方について考えてみる。 4.余分なメッセージの消去イベントログのシステムは、メッセージの出力元(ソース)とイベントIDから、リソースを検索してそこからメッセージを取り出そうと試みる。そして、リソースを検索した結果該当するリソースを見つけられなかった場合に、上図のようなメッセージを表示する。 だから、検索するリソースとやらを準備してやれば、上図のようなメッセージが表示されることはなくなるはずである。 必要なりソースとかいうものは、下記の手順に従い生成する。 4.1 mcファイルの作成イベントIDに対応するメッセージを記述した、mcファイルを作成してやる。mcファイルのフォーマットについては、下記のページに記載されているが、何分英語なので何を言っているのか判らない。 とりあえずここでは、下記のように記述する。 LanguageNames=(Japanese=0x0411:MSG00411) MessageId=0 SymbolicName=name0 Language=Japanese メッセージは「%1」です。 . LanguageNamesには、その後のLanguageにJapaneseと指定できるようにするために記述する必要がある。とりあえず、ファイルの先頭に記述しなければならない。 MessageIdにはイベントIDに指定する番号を記述する。 SymbolicNameには、何でもいいから適当に名前を付ける。 Languageには言語を指定する。とりあえず、俺は日本人なのでJapaneseと指定しておく。 その後の%1の部分には、イベントログとして出力するメッセージを記述する。%1は、ReportEvent関数のlpStringsに指定した文字列の0番目のものに置換される。すなわち、%1はlpStrings[0]に、%2はlpStrings[1]に、%3はlpStrings[2]に置き換えられることになる。 最後のピリオド(ピリオドだけの行)は、メッセージの終わりを示している。 とりあえずこういうファイルを作成して、evtest2.mcという名前で保存するものとする。 4.2 コンパイル下記のコマンドで、作成したmcファイルをコンパイルする。
なお、通常ではmc.exeが入っているところにはパスは通っていない。俺の環境ではC:\Program Files\Microsoft Visual Studio\VC98\Binにあったが、これは人によって異なるはずだ。とりあえず、mc.exeという名前でディスク内を検索して、見つかったディレクトリにパスを通せば何とかなるのではないだろうか。 上記のコマンドでコンパイルに成功すると、下記の3つのファイルがカレントディレクトリ内に生成される。
次に、生成されたrcファイルを、rcコマンドでコンパイルする。
rcファイルのコンパイルに成功すると、下記のファイルが生成される。
次は、このRESファイルをリンクしてDLLを生成してやる。
当然だが、PATHの設定は・・・・・・って、もういいよな。 上記の処理が全てうまくいくと、メッセージのリソースだけを含んだevtest2.dllというDLLが生成される。 4.3 インストール4.2で生成したDLLをインストールしてやり、イベントログのシステムから検索できるように設定してやる。 インストールするには、レジストリのHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\EventLog\Application\{アプリケーション名}に、 {アプリケーション名}には、2.1のRegisterEventSource関数のlpSourceNameに指定する値を設定する。 4.4 表示これで一応、不要なメッセージは表示されなくなるはずである。再度、先ほどのメッセージをイベントビューアで表示してやると、下記のようになる。 5.複数種類のメッセージを表示するたとえば、イベントログを出力するプログラムを下記のように変更する。 const char *pMsg0[2] = { "メッセージ0−0", "メッセージ0−1" }; const char *pMsg1[2] = { "メッセージ1−0", "メッセージ1−1" }; HANDLE h = RegisterEventSource( NULL, "myapp" ); ReportEvent( h, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 2, 0, pMsg0, NULL ); ReportEvent( h, EVENTLOG_ERROR_TYPE, 0, 1, NULL, 2, 0, pMsg1, NULL ); DeregisterEventSource( h ); でもって、mcファイルを下記の様に記述する。 LanguageNames=(Japanese=0x0411:MSG00411) MessageId=0 SymbolicName=name0 Language=Japanese イベントID=0のメッセージです。「%1」「%2」 . MessageId=1 SymbolicName=name0 Language=Japanese イベントID=1のメッセージです。「%1」「%2」 . つまり、イベントIDとして0番と1番の二つを使用し、かつ、それぞれメッセージ中に二つの文字列を挿入するものとする。 このようにした場合、イベントログには下記のように出力される。 つまり、メッセージ中のロケールに依存する固定文字列の部分はリソースに記述して、可変部分はプログラムから出力するようにしてやればいい。 また、メッセージの種類が多ければ使用するイベントIDの数を増やしてやればよくって、一つのメッセージ中に含まれる可変部分の数が多いのであれば、メッセージ中の%1・%2・・・の個数と、ReportEvent関数のlpStringsに指定する文字列の個数を増やしてやればいいと言うことになる。 6.最後に上記の4で作成したDLLとレジストリの設定は、イベントログにメッセージを書き込むときではなく、イベントログを参照する際に使用される。 つまり、生成したDLLをアプリケーションとともに配布する必要があり、インストーラでレジストリ設定を行ってやる必要があるということだ。 また、アプリケーションが動作するPCとイベントログを参照するPCが異なる可能性がある場合には、イベントログを参照する側のPCにも、上記のDLLとレジストリ設定が必要になる。アプリケーションを配布する際には、そういったことにも十分に検討しておく必要がある。 (なお最後になるが、このページで公開している画像は全てホスト名を消してある。その辺についてはご了承下さい) |
||||||||||||||||
連絡先 - サイトマップ - 更新履歴 Copyright (C) 2000 - 2016 nabiki_t All Rights Reserved. |