レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API イベント ログの概要

Microsoft(R) Windows(TM) 用のアプリケーションでエラーが発生した場合、 システム管理者やサポート スタッフは、 エラーの原因を調べて、 失われたデータを修復し、 エラーが再発しないようにしなければなりません。メモリ不足やディスク試行の超過などをアプリケーションが記録していれば、 その情報の記録 (「イベント ログ」と呼ばれます) は、 エラーの発生した状況を判断する助けになります (イベント ログは、 Windowsオペレーティング システムやシステム サービスにも適用されます)。システム管理者は、 イベント ログを定期的に調べることによって、 重大なダメージを被る前に問題を特定できます。

多くのアプリケーションは、 エラーやイベントを独自のエラー ログに記録します。この独自のログの形式はさまざまで、 表示には異なるユーザー インターフェイスが必要になり、 統合して1つのレポートにすることはできません。しかし、 このようなログは、 同様な情報を格納しており、 よく似た方法でログ データを記録しています。

Windowsのイベント ログによって、 アプリケーション (およびWindows) が重要なソフトウェア イベントやハードウェア イベントを記録する方法が標準化されます。また、 ログを調べるためのユーザー インターフェイスやプログラミング インターフェイスも標準化されます。イベント ログによって、 さまざまなイベントを統合して単一のわかりやすい情報にまとめることができます。

次に示すトピックでは、 Win32のイベント ログについて説明しています。

イベント ログのモデル

ログのガイドライン

ログの効率とサイズ

イベント ログ管理情報

イベント ログ操作

イベント ログ エントリ

特別な場合の処理

イベント ログの使用

ソースのレジストリへの追加

イベントの報告

イベント ログの問い合わせ

イベント ログの読み取り

イベント ログ関数

イベント ログのモデル

イベント ログは、 Windowsオペレーティング システムやWindowsで動作するアプリケーションに代わって、 重要なイベントを記録します。一般に、 ハードウェアやソフトウェアの問題を調べるのに役立つ情報だけを記録してください。

イベントには、 情報、 警告、 エラーがあります。どのイベントにも共通データが厳密に定義されており、 オプションでイベント固有のデータを追加できます。たとえば、 情報イベントは、 サービスが開始したことを知らせます。警告は、 ディスク容量の不足など、 修復可能な問題に使われます。エラーは、 アプリケーションの失敗など、 修復不可能な状況に使われます。セキュリティ サービスは、 イベントをログするほかに、 監査の成功と失敗もログします。

ログのガイドライン

ログ関数は汎用であるため、 ログすべき情報の種類はよく考えてください。ログには、 ディスク容量やログに要する時間などのリソースが必要です。イベント ログは、 トレース機能として使うためのものではありません。

ログは、 問題が発生した後で問題の診断に使われるイベント情報を記録するのに使ってください。たとえば、 メモリ不足状況がログされていれば、 システム管理者は、 コンピュータにメモリを追加して問題を解決できます。

イベント ログは、 次のような場合に役立ちます。

ディスク ドライバがディスクの不良セクタを発見したとき。何度か試行を行えばそのセクタを読み書きできるようになる場合もありますが、 そのセクタはいずれ不良になると考えられます。続行できる場合は、 ディスク ドライバは警告をログしてください。続行できなければ、 エラーをログしてください。

ファイル システムが不良セクタを発見して修正したとき。このようなイベントが大量に発生する場合は、 ディスク自体が壊れかけている可能性があります。ファイル システムは、 警告イベントをログします。

ディスク コントローラのタイムアウト、 パラレル ポートの電源断、 ネットワークやシリアル カードのデータ エラーなどのハードウェア上の問題をデバイス ドライバが発見したとき。このようなイベントに関する情報をログしておけば、 ハードウェアの問題の診断に役立ちます。デバイス ドライバは、 ハードウェアの問題をログします。

アプリケーションがリソースの問題を発見したとき。アプリケーションやデバイス ドライバでは、 効率を低下させるメモリ不足状況が (コードのバグなどによって) 発生する場合があります。メモリ割り当てが失敗したときにイベントをログすることによって、 問題の原因を解明する手がかりが得られます。アプリケーションは、 リソースの問題をログします。

(データベース サーバーや通信サーバーなどの) サーバー アプリケーションは、 ユーザーのログオン、 データベースのオープン、 ファイル転送の開始などのイベントを記録します。また、 サーバーは、 アプリケーションが使用しているリソースの個数、 発生したエラー (ファイルがアクセス不可能なときや、 ホスト プロセスが接続されていないときなど)、 データベースの損傷 (ツリーの特定のパスの末端ノードを発見できないときなど)、 ファイル転送が正常に終了したかどうかなどを記録します。このような情報は、 すべて、 サービス マンやアプリケーションの開発者にとって有益な情報です。アプリケーションは、 このようなイベントを記録し、 情報イベントとしてログします。

ログの効率とサイズ

イベント ログに必要なディスク容量や、 イベントをログするための呼び出しを行うために必要なアプリケーションのオーバーヘッドの量は、 アプリケーションがログしようとする情報の量によって異なります。このため、 必要な情報だけをログし、 ログによる性能の低下の影響が大きいときは、 アプリケーションのメインの処理部分ではなくエラー処理部分でイベント ログ呼び出しを行ってください。

イベント ログ管理情報

イベント ログ管理情報は、 構成データベースのServicesキーに格納されており、 システム管理者が修正できます。

構成情報の構造を次に示します。

HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
EventLog
Application
Security
System

EventLogキーには、 「ログファイル」と呼ばれるサブキーがいくつかあります。デフォルトのログファイルには、 ApplicationSecurity Systemがあります。各ログファイル サブキーには、 「ソース」と呼ばれるサブキーがあります。すでにログファイルの名前として使われている名前はソース名には使えません。また、 ソース名は階層構造にはできません (円記号 [\] はレジストリ キーには使えません)。各ソース エントリには、 次の表のような、 イベントのソースに固有の情報があります。

説明

EventMessageFile イベント識別子メッセージ ファイルのパスとファイル名を示します。この種類の値は、 REG_EXPAND_SZです。

CategoryMessageFile カテゴリ メッセージ ファイルのパスとファイル名を示します。カテゴリ メッセージ文字列とイベント識別メッセージ文字列は同じファイルにあってもかまいません。この種類の値は、 REG_EXPAND_SZです。

CategoryCount サポートされているカテゴリの個数を示します。この種類の値は、 REG_DWORDです。

TypesSupported サポートされている種類のビットマスクを示します。この種類の値は、 REG_DWORDです。

アプリケーションがRegisterEventSource関数やOpenEventLog関数を使ってイベント ログのハンドルを取得すると、 イベント ログ サービスは、 指定された名前をレジストリで検索します。たとえば、 Applicationログファイルで、 Microsoft(R) SQL Server(TM) Microsoft(R) Excelのソースを構成しているとします。アプリケーションがApplication SQL Excelなどのソース名を指定してRegisterEventSourceOpenEventLogを呼び出すと、 イベント ログ サービスは、 Applicationログファイルを識別するハンドルを返します。

レジストリに新しいソース キーを追加せずにイベントをログするには、 Applicationイベント ログを使ってください。レジストリにないソース名を指定してRegisterEventSourceを呼び出すと、 イベント ログ サービスは、 デフォルトでApplicationログファイルを使います。しかし、 このログファイルにはメッセージ文字列ファイルやカテゴリ文字列ファイルがないため、 イベント ビューアは、 イベント識別子やカテゴリを対応する文字列にマップできません。このため、 アプリケーションに固有のソース名をレジストリに追加してください。これによって、 イベントの識別子とカテゴリのメッセージ ファイルを指定できます。アプリケーションがイベントを報告すると、 イベント ビューア(EVENTVWR.EXE) は、 報告されたイベントのイベント識別子とカテゴリのパラメータをメッセージ ファイルの対応する文字列にマップします。

アプリケーションやサービスは、 ソース名をApplicationログファイルに追加してください。デバイス ドライバは、 ソース名をSystemログファイルに追加してください。

レジストリの使用について詳しくは、 レジストリと初期化ファイルの概要を参照してください。メッセージ ファイルの作成と使用について詳しくは、 メッセージ コンパイラのマニュアルを参照してください。

イベント ログ操作

イベント ログに対する5つの操作を次の表に示します。

操作 関数

バックアップBackupEventLog

消去ClearEventLog

問い合わせ GetOldestEventLogRecord, GetNumberOfEventLogRecords

読み取りReadEventLog

書き込みReportEvent

また、 OpenEventLog関数、 OpenBackupEventLog関数、 RegisterEventSource関数、 DeregisterEventSource関数、 CloseEventLog関数は、 イベント ログ ハンドルをオープンまたはクローズします。これによって、 関数セットのオブジェクト指向モデルが実現されるとともに、 ログに対して複数の操作を行う場合に性能が若干向上します。

OpenEventLog関数やReportEvent関数のオプションのパラメータとしてサーバー名を指定すると、 リモート サーバーに対して操作を実行できます。OpenEventLogは、 ログの読み取りや管理操作 (バックアップ、 消去、 問い合わせ) に使ってください。また、 RegisterEventSourceは、 ログの書き込みに使ってください。

イベント ログ エントリ

次に、 ログがとられるイベントのパラメータについて説明します。

ソース

ソースとは、 イベントをログするソフトウェアの名前です。ほとんどのアプリケーションでは、 ソースはアプリケーション名です。また、 大きなアプリケーションでは、 その一部の名前になる場合もあります。

イベント識別子

イベント識別子は、 ユーザーに表示する説明文字列をメッセージ ファイルから取得するときに使われます。また、 サポート スタッフもイベント識別子を使います。ユーザーがソースとイベント識別子を指定していれば、 サポート スタッフは、 どのようなイベントが発生したかを正確に知ることができます。イベント識別子は、 各ソースに固有です。

種類

Windows NTでは、 5種類のイベント ログを定義しています。イベントの種類を組み合わせることはできないため、 各イベントの種類はいずれか1つだけです。

イベントの種類 説明

情報情報イベントは、 回数が少ない重要な操作の正常終了に使われます。たとえば、 Microsoft SQL Serverは、 正常にロードされると、 SQL Server has started情報イベントをログします。重要なサーバー サービスの場合はこのログは適切ですが、 (Microsoft Excelなどの) デスクトップ アプリケーションは、 起動するたびにイベントをログしないでください。情報イベントは、 トレースには使わないでください。

警告警告イベントは、 重要ではないもののエラーなどの原因となる可能性がある問題に使われます。リソースの消費などには、 警告イベントを使ってください。たとえば、 ディスク容量が不足しているときは、 警告をログしてください。機能やデータの損失なしに修復できるエラーは、 警告になります。

エラーエラー イベントは、 重要な問題が発生したことをユーザーに知らせるために使われます。通常、 エラーは、 機能やデータが失われることを示しています。たとえば、 システムの起動時にサービスをロードできないときは、 エラー イベントをログしてください。

成功監査成功監査イベントは、 監査アクセス試行が正常に終了したときに発生するセキュリティ イベントです。たとえば、 ログオン試行の正常終了は成功監査イベントです。

失敗監査失敗監査イベントは、 監査アクセス試行が失敗したときに発生するセキュリティ イベントです。たとえば、 ファイル オープン試行の失敗は失敗監査イベントです。

カテゴリ

カテゴリは、 イベントを組織化して、 イベント ビューアでフィルタできるようにするために使われます。各ソースでは、 それぞれ異なる個数のカテゴリと、 それに対応するテキスト文字列を定義できます。カテゴリには、 1から始まる連続した番号を付けてください。イベント ビューアでは、 ソースでフィルタし、 さらにソース内のカテゴリでフィルタできます。セキュリティ システムは、 ログオンまたはログオフ、 ファイル システム アクセス、 特権動作、 セキュリティ ポリシー内の変更などのカテゴリをログします。また、 どのイベントをログすべきかが明らかになるにつれて、 システムのほかの部分のカテゴリも追加されます。アプリケーションは、 専用のカテゴリを定義できます。

文字列

文字列は、 説明文字列のプレースホルダに値を設定するのに使われる、 言語に依存しない (ローカライズされない) 省略可能な文字列です。文字列はローカライズされないため、 このフィールドには、 数値や1単語で構成される文字列 (ファイル名やユーザー名など) のような言語に依存しない文字列だけを格納してください。文字列の長さは、 32Kバイト-1文字以下でなければなりません。

複数の文字列を結合して長い説明文字列を作成したり、 文になった文字列を使わないでください。挿入文字列はテキストではなくデータとして扱われます。たとえば、 次の例のようなことは行わないでください。

Str1$ = successfully
Description = "User was %1 added to database."

次の3つの理由のため、 この2つの文字列を使って文字列User was successfully added to database.(代替文字列はStr1$ = not) を作成してはいけません。

1. successfullynotをローカライズしなければならなくなります。

2. 挿入文字列を言語固有のメッセージ ファイルから取得できても、 イベントを表示するときではなくイベントのログ時に文字列の置き換えが行われるため、 イベントを表示するときに言語が異なっていると正しく表示されません。

3. 動詞や形容詞の置き換えは、 ほかの言語でもただしく行われるとは限りません。上記のような例では、 2つの独立したイベントを使ってください。

文字列の適切な使い方を次の例に示します。

Str1$ = "c:\testapp.c".
Description = "Access denied. Attempted to open the file %1"

記述

各イベントには、 ソースが登録したメッセージ ファイル内にある説明が関連付けられています。説明は、 問題の状況と行うべき動作をユーザーに理解させるためのものです。説明は、 ユーザーが自分自身で問題を解決できるようにするためのものであり、 サポート スタッフや開発者向けではありません。説明は、 明確に、 わかりやすくして、 文化固有の言い回しは避けてください。イベントのデータ フィールドにプライベートなデータを格納するときは、 データ フィールドの内容に関する説明を記述の最後に付加してください。たとえば、 ネットワーク ソフトウェアの場合は、 (The SMB is the event data).のような文字列を追加します。この例のように、 通常、 注釈はかっこで囲んでください。複数のアプリケーションが同じメッセージ ファイルを共有でき、 メッセージ ファイルには、 イベント識別子とイベント カテゴリの両方の説明を格納できます。

説明文字列をイベント識別子でインデックスすることによって、 イベント ビューアは、 イベント識別子に基づいてイベントのテキストを表示できます。説明はすべてローカライズされ、 言語によって異なります。説明文字列には、 省略可能な挿入文字列のプレースホルダを含めることができます。各プレースホルダは、 パーセント記号と、 置換する文字列のインデックス番号で表されます。たとえば、 プレースホルダ%11番目の挿入文字列で置き換えられ、 %55番目の文字列で置き換えられます。

データ

各イベントには、 イベント固有のデータを関連付けることができます。イベント ビューアはイベントに関する情報を持っていないため、 追加データは16進ダンプとテキスト ダンプの形式でしか表示しません。イベント固有のデータは、 サポート技術者や開発者に取って役立つときだけ使ってください。たとえば、 情報が設定された構造体のデータならば、 アプリケーションの失敗を技術者がデバッグするのに役立ちます。

イベント固有データの例として、 ネットワーク イベントがあります。ネットワーク制御ブロック (NCB) は、 イベント固有データとして格納されます。どのような場合でも、 説明文字列の最後の部分には、 イベント固有データとして利用可能な情報に関する注意を付加してください。サード パーティのアプリケーションは、 イベント ビューアに関係なくアプリケーションが独自に処理する情報をデータ フィールドに格納できます。たとえば、 特定のアプリケーションのイベント専用のビューアを作成できます。また、 ログファイルを走査し、 イベント固有データの情報から報告を作成するプログラムを作成することもできます。

EVENTLOGRECORD構造体

ReadEventLog関数は、 EVENTLOGRECORD構造体を使ってイベント ログ情報を書式化します。この構造体には、 イベント記録の内容に関する情報が設定されています。実際のイベント記録情報は、 ReadEventLog関数が返すバッファのこの構造体の後にあります。

特別な場合の処理

イベント ログ関数の呼び出しは、 分割できない操作です。イベント ログを読み取る場合は、 イベント記録全体が返されます。イベントをログする場合は、 各イベント記録が完全な記録としてログに書き込まれることが保証されます。イベント ログ サービスが特別な場合を処理する方法を次に示します。

同じログに対して複数の書き込み呼び出しが発生したとき

イベント ログ サービスは、 操作を順番に並べて、 イベント ログをログファイルに順番に書き込みます。

読み取りと書き込みが同時に発生したとき

行われる処理は、 現在の読み取り操作のファイル内の位置によって異なります。読み取り位置がファイルの終端の場合、 (書き込み操作がまだ実行されていなければ) 読み取り操作はEOFの状態を返して失敗します。書き込み操作でログに記録を書き込んだときは、 その記録を返します。

ログを読み取る前に消去したとき

読み取り操作は、 ファイルが消去された後はEOF状態を返します。

ログに書き込む前にログを消去したとき

消去操作は、 ログファイルを切り捨てます。その後の書き込み操作は、 ログの先頭に新しい記録を書き込みます。

イベント ログの使用

次に、 イベント ログに関する作業を示しています。

レジストリへのソースの追加

イベントの報告

イベント ログの問い合わせ

イベントの読み取り

イベント ログのレジストリ キーを表示するには、 Windows Registration Database Editor (REGEDIT.EXE) を使ってください。また、 イベント ログを表示するには、 イベント ビューアを使ってください。

レジストリへのソースの追加

ソース名をレジストリに追加しないときは、 デフォルトのApplicationイベント ログを使ってください。しかし、 ソースを登録してメッセージ ファイル名を指定しなければ、 イベント ビューアがイベント識別子をメッセージ文字列のマップできません。

新しいソース名をレジストリに追加するには、 Applicationキーの下に新しいレジストリ サブキーをオープンし、 そのサブキーにレジストリ値を追加してください。次に示すコード例は、 SamplAppという名前の新しいソースをオープンし、 新しいサブキーにメッセージ ファイル名とサポートする種類のビットマスクを追加します。

HKEY hk;
DWORD dwData;
UCHAR szBuf[80];

/*
* Add your source name as a subkey under the Application
* key in the EventLog service portion of the registry.
*/

if (RegCreateKey(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSe_\\Services\
\\EventLog\\Application\\SamplApp",
&hk))
ErrorExit("could not create registry key");

/* Set the Event ID message-file name. */

strcpy(szBuf, "%SystemRoot%\\System\\SamplApp.dll");

/* Add the Event ID message-file name to the subkey. */

if (RegSetValueEx(hk,/* subkey handle*/
"EventMessageFile",/* value name*/
0,/* must be zero*/
REG_EXPAND_SZ, /* value type */
(LPBYTE) szBuf,/* address of value data */
strlen(szBuf) + 1))/* length of value data*/
ErrorExit("could not set event message file");

/* Set the supported types flags. */

dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE;

if (RegSetValueEx(hk,/* subkey handle*/
"TypesSupported",/* value name*/
0,/* must be zero*/
REG_DWORD,/* value type*/
(LPBYTE) &dwData,/* address of value data*/
sizeof(DWORD)))/* length of value data*/
ErrorExit("could not set supported types");


RegCloseKey(hk);

イベントの報告

ソース名をレジストリに追加したら、 次の例のように、 RegisterEventSource関数を使ってApplicationイベント ログのハンドルを取得します。この例では、 ハンドルを取得して、 イベントをログに追加しています。

LPSTR aszMsg[] = {
"SamplApp",
};

HANDLE h;

h = RegisterEventSource(NULL, /* uses local computer*/
"SamplApp");/* source name*/
if (h == NULL)
ErrorExit("could not register event source");

if (!ReportEvent(h,/* event log handle */
EVENTLOG_ERROR_TYPE,/* event type*/
0,/* category zero*/
0x1003,/* event identifier */
NULL,/* no user security identifier */
1,/* one substitution string*/
0,/* no data */
(LPTSTR *) aszMsg,/* address of string array*/
NULL))/* address of data*/
ErrorExit("could not report event");

DeregisterEventSource(h);

イベント ログの問い合わせ

次に示すコード例は、 Applicationイベント ログとSystemイベント ログとの現在のイベント記録の個数を表示します。

/* Open the System log. */

h = OpenEventLog(NULL,/* uses local computer*/
"System");/* source name*/
if (h == NULL)
ErrorExit("could not open System event log");

/* Get the number of records in the System event log. */

if (!GetNumberOfEventLogRecords(h, &cRecords))
ErrorExit
("could not get number of records");

printf("There are %d records in the System event log.\n", cRecords);

CloseEventLog(h);

/* Open the Application log. */

h = OpenEventLog(NULL,/* uses local computer*/
"Application");/* source name*/
if (h == NULL)
ErrorExit("could not open Application event log");

/* Get the number of records in the Application event log. */

if (!GetNumberOfEventLogRecords(h, &cRecords))
ErrorExit("could not get number of records");

printf("There are %d records in the Application event log.\n",
cRecords);

CloseEventLog(h);

イベント ログの読み取り

次に示すコード例は、 Applicationログファイルの記録をすべて読み取って、 各イベント ログ エントリのイベント識別子、 イベントの種類、 ソース名を表示します。

EVENTLOGRECORD *pevlr;
BYTE bBuffer[BUFFER_SIZE];
DWORD dwRead, dwNeeded, cRecords, dwThisRecord = 0;

/* Open the Application event log. */

h = OpenEventLog(NULL,/* uses local computer*/
"Application");/* source name*/
if (h == NULL)
ErrorExit("could not open Application event log");

pevlr = (EVENTLOGRECORD *) &bBuffer;

/*
* Opening the event log positions the file pointer
* for this handle at the beginning of the log.
*
* Read records sequentially until there
* are no more.
*/

while (ReadEventLog(h,/* event log handle */
EVENTLOG_FORWARDS_READ |/* reads forward*/
EVENTLOG_SEQUENTIAL_READ, /* sequential read */
0, /* ignored for sequential reads */
pevlr,/* address of buffer */
BUFFER_SIZE, /* size of buffer*/
&dwRead,/* count of bytes read*/
&dwNeeded)) { /* bytes in next record*/

while (dwRead > 0) {

/*
* Print the event ID, type, and source name.
* The source name is just past the end of the
* formal structure.
*/

printf("%02dEvent ID: 0x%08X ",
dwThisRecord++, pevlr->EventID);
printf("EventType: %d Source: %s\n",
pevlr->EventType, (LPSTR) ((LPBYTE) pevlr +
sizeof(EVENTLOGRECORD)));

dwRead -= pevlr->Length;
pevlr = (EVENTLOGRECORD *)
((LPBYTE) pevlr + pevlr->Length);
}

pevlr = (EVENTLOGRECORD *) &bBuffer;
}

CloseEventLog(h);

イベント ログ関数

イベントのログに関する関数を次に示します。

BackupEventLog

ClearEventLog

CloseEventLog

DeregisterEventSource

GetNumberOfEventLogRecords

GetOldestEventLogRecord

NotifyChangeEventLog

OpenBackupEventLog

OpenEventLog

ReadEventLog

RegisterEventSource

ReportEvent

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします