初めに
OS やフレームワークの API とかを叩いたりするとき、ドキュメントのサンプルプログラムがおかしいと悲劇が生じる。
かつて、Windows API のドキュメントで、
- 終了時に GetWindowsPos で Windows の位置を取得
- 次回起動時に上記の値を元に SetWindowsPlacement でWindows を配置
というサンプルコードが掲載されていた。
この結果、
- タスクバーを画面の上にしている場合、あるいはタスクバーの類を画面の上に配置している場合、起動時のウィンドウの位置が徐々に上がっていくアプリが多数発生した。特に Word95 とかの Microsoft Office 95 もこのバグを発生させていた。
今はどうもこの GetWindowsPos という API は廃止されてしまったようだ。
この件について僕が twitter でとったアンケートの結果はこちらです。
まぁ、これは本題とは直接関係ない昔話。
本題
さて本題。
.NET Framework でイベントログに書き込むときはSystem.Diagnostics名前空間のSystem.Diagnostics.Eventlog クラスを用いる。
ネットで検索してひっかかる、実際にイベントログに書き込む WriteEntry メソッドのサンプルは、だいたい次のような感じになっていた。
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const string EVENTSOURCE = "EventSource1";
const string MESSAGE = "EventLogTest";
if (!EventLog.SourceExists(EVENTSOURCE))
{
EventLog.CreateEventSource(EVENTSOURCE, null);
}
EventLog.WriteEntry(EVENTSOURCE, MESSAGE);
}
}
}
なお、MSDN で 2017/12/01 に WriteEntry の最新のサンプルを確認したら少し変わってつぎのような感じになっていた。
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const string EVENTSOURCE = "EventSource1";
const string MESSAGE = "EventLogTest";
if (!EventLog.SourceExists(EVENTSOURCE))
{
// 遅延があるからイベントソース作成後即座に使うべきではない
EventLog.CreateEventSource(EVENTSOURCE, null);
// イベントソース登録のため、いったん終了する。
return;
}
EventLog.WriteEntry(EVENTSOURCE, MESSAGE);
}
}
}
ところが、これらのサンプルはよくない。
Sample1、Sample1a で登場する EventLog クラスのメソッドの動作の概要は次の通り。
メソッド | 動作概要 |
---|---|
SourceExists | イベントソースが登録されているかを返す。管理者権限が無ければ例外エラーになる。 |
CreateEventSource | イベントソースを登録する。 既に対象のイベントソースが登録されているならば例外エラーになる。 また、管理者権限が無ければ例外エラーになる。 |
WriteEntry | 対象のイベントソースが登録されてる場合、管理者権限の有無に関係なくイベントログに指定したイベントソースで書き込む。 対象のイベントソースが登録されていない場合で管理者権限がある場合、新たにイベントソースを登録してそのイベントソースでイベントログに書き込む。 対象のイベントソースが登録されていない場合で管理者権限が無い場合例外エラーになる。 |
つまり、
Sample1、Sample1a は管理者権限が無ければ動作しない
のである。
・・・でも、ただ EventLog に書き込むだけで管理者権限が必要っておかしいですよね?
最新の Visual Studio は動かす際に管理者権限が無くても良くなっているが、一昔前の Visual Studio は動かす時に管理者権限が必要だったため、 Sample1、Sample1a のバグ(僕はこれはバグであると断じる)は
単体テストで OK で、結合テストで NG
となる可能性が高いものである。
前にいたプロジェクトで Sample 1のようなイベントソースの存在チェックをおこなっているバグがに遭遇したことがあったし、実際にググったら Sample 1 のようなものしか出てこない。(2017-12-1 現在)
正しくは次のようにチェックをせずに直接 WriteEntry を呼び出す必要がある。
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
const string EVENTSOURCE = "EventSource1";
const string MESSAGE = "EventLogTest";
EventLog.WriteEntry(EVENTSOURCE, MESSAGE);
}
}
}
Sample1、Sample1a、Sample 2 の動作の違いは次のようになる。
管理者権限 | イベントソース | Sample1 | Sample1a | Sample2 |
---|---|---|---|---|
ある | 登録済み | イベントログに書き込まれる | イベントログに書き込まれる | イベントログに書き込まれる |
ある | 未登録 | イベントソースを登録してイベントログに書き込まれる | イベントソースの登録のみ | イベントソースを登録してイベントログに書き込まれる |
ない | 登録済み | 権限無しの例外エラー | 権限無しの例外エラー | イベントログに書き込まれる |
ない | 未登録 | 権限無しの例外エラー | 権限無しの例外エラー | 権限無しの例外エラー |
したがって、.NET Framework でイベントログに書き込むプログラムを作る場合、次の点に注意する。
- イベントソースの存在チェックを行ってはいけない。
- イベントソースは構築時に別プログラムや PowerShell Script を管理者権限のある状態で動かして登録する、管理者権限がある状態で動かすインストーラに組み込むなどして事前に登録しておくようにする。
終わりに
元は ここに書いていたのだが、アクセス数が伸びないのでこっちに転載。