Application Error - ID:1000
アプリケーションの実行中に何らかのエラーが発生して異常終了した場合、イベントログにApplication ErrorのソースでID:1000のログが記録されます。詳細内容の典型的な例は以下のとおりです。
障害が発生しているアプリケーション名: invalidheap.exe、バージョン: 0.0.0.0、タイム スタンプ: 0x6862ac6f
障害が発生したモジュール名: ucrtbase.dll、 バージョン: 10.0.26100.4202、タイム スタンプ: 0xbd317707
例外コード: 0xc0000409
フォールト オフセット: 0x0008ad02
フォールト プロセス ID: 0x337C
アプリケーションのフォールトの開始時刻: 0x1DBE9D33AEED1CF
Faulting アプリケーション パス: C:\source\invalidheap\Release\invalidheap.exe
Faulting モジュール パス: C:\WINDOWS\System32\ucrtbase.dll
Report Id: dc7ed2f5-17aa-4df7-9b55-84307a9822c6
Faulting パッケージの完全名:
Faulting パッケージ相対アプリケーション ID:
各フィールドの値の意味
フィールド | 説明 |
---|---|
障害が発生しているアプリケーション名 | 異常終了したアプリケーションの名前とファイルバージョン、タイムスタンプが記録されます。タイムスタンプはexeファイルのIMAGE_FILE_HEADER.TimeDateStampフィールドの値で、UNIXエポック(1970年1月1日 00:00:00 UTC)からの秒数となります。PowerShellで(New-Object DateTime (1970,1,1,0,0,0)).AddSeconds(0x6862ac6f).ToLocalTIme() のように値を渡せば"2025年7月1日 0:25:35"といった具合にデコードできます。ただし、ブログ記事によると、ビルドの都度変わるタイムスタンプのようなフィールドは「再現性のあるビルド」の妨げになるため、Windows 10以降のモジュールはハッシュ値が格納されているようです。 |
障害が発生したモジュール名 | 例外を発生させた命令のアドレスにロードされているモジュールの名前とファイルバージョン、タイムスタンプが記録されます。それぞれの値はアプリケーション名と同様です。ただし、スタックバッファーオーバーランによるリターンアドレス破壊などでモジュールが配置されていないアドレスが実行された場合、モジュール名がunknownになります。 |
例外コード | 発生した例外が記録されます。代表的な例外を以下に記載します。
|
フォールト オフセット | 障害が発生したモジュールを起点とする例外が発生した命令のアドレスへのオフセットが記録されます。後述しますが、実際のモジュールとオフセットが分かれば、例外が発生したアドレスのメソッド名などを特定することが可能です。 |
フォールト プロセス ID | 障害が発生したプロセスのプロセスIDが記録されます。 |
アプリケーションのフォールトの開始時刻 | 障害が発生した時刻が記録されます。こちらはFILETIME形式ですので、PowerSHellで[System.DateTime]::FromFileTimeUtc(0x1DBE9D33AEED1CF).ToLocalTime() のように値を渡せば"2025年7月1日 0:25:37"といった具合にデコードできます。 |
Faulting アプリケーション パス | 障害が発生したアプリケーションの完全なパスが記録されます。 |
Faulting モジュール パス | 障害が発生したモジュールの完全なパスが記録されます。32bitプロセスの場合はWOW64でリダイレクトされたパスとなってるため、このパスを元にモジュールを取得する場合は注意が必要です。 |
Report Id | Windowsエラー報告で使用される識別子と思われます。 |
Faulting パッケージの完全名 | クラッシュしたアプリケーションがUWPアプリの場合にのみ記録されるアプリケーションパッケージ名です。(例:windows.immersivecontrolpanel_10.0.2.1000_neutral_neutral_cw5n1h2txyewy) |
Faulting パッケージ相対アプリケーション ID | クラッシュしたアプリケーションがUWPアプリの場合にのみ記録されるアプリケーションの識別子です。 (例:microsoft.windows.immersivecontrolpanel) |
イベントログの内容から未処理例外を発生させたメソッドを特定する
イベントログに記録されているモジュールの実物を問題が発生した環境から入手することができれば、以下の手順でオフセットの値からメソッド名を特定することが可能です。特定にはシンボルファイルが必要になりますので、モジュールがMicrosoft製の場合、Microsoftが公開しているパブリックシンボルサーバーへアクセス可能な環境で作業してください。
- WinDbgを起動します。
- メニューから[ファイル]-[Start debugging]-[Open dump file]を選択し、[Browse]ボタンをクリックします。
- ファイルを[開く]ダイアログで、ファイルの種類を[Image files (*.dll, *.exe)]に変更し、あらかじめ問題が発生した環境から入手しておいたモジュールを選択して開きます。
- [Open dump file]の画面に戻りますので、[Target architecture]を[Autodetect]にして[Open]ボタンをクリックします。
- コマンドが入力可能になったら、
.sympath srv*https://msdl.microsoft.com/download/symbols
コマンドを入力してMicrosoftのシンボルサーバーを指定します。 .reload /f <モジュールファイル名>
コマンドを入力してシンボルを取得します。
例: .reload /f ucrtbase.dllu <モジュール名>+<オフセット>
コマンドを入力します。
例: u ucrtbase+0x0008ad02- 例外を発生させた関数名は
_invoke_watson
であったことが分かります。ここで実行しているint 29h
はfastfailと呼ばれる命令で、回復できない問題を検出して明示的にプロセスを終了させるためのものです。ドキュメントによるとコード引数はecx
やrcx
レジスタに格納されます。 uf <モジュール名>+<オフセット>
コマンドを実行して命令を含む関数全体を逆アセンブルすると、int 29h
が実行されたとき直近の命令の状態からecx
レジスタには"5"が格納されていたことが想定されます。ブログ記事によると"5"はFAST_FAIL_INVALID_ARGを指しており、パラメーターが無効だったため例外に至った可能性が示唆されます。
ちなみに、今回イベントログが記録される状況となった実際のプログラムでは、以下のようにsprintf_s関数でバッファーのサイズを超える書き込みを発生させています。sprintf_s関数はパラメーターの検証でこの状況を検出した場合、無効なパラメーターハンドラーを呼び出し、ハンドラーが指定されていない場合はFAST_FAIL_INVALID_ARGでプロセスを強制終了します。
#include <stdio.h>
int main()
{
char buf[4];
sprintf_s(buf, "%d", 123456);
}
なお、上記のケースではイベントログに記載されている「障害が発生したモジュール」はucrtbase.dllとなっているので、ucrtbase.dll自身の問題でアプリケーションが異常終了したと判断してしまうかもしれません。しかし実際にはそうではなく、上記のケースでは明らかにucrtbase.dllを使用したアプリケーション側の実装に問題があります。「障害が発生したモジュール」は、あくまでも「例外を発生させた命令のアドレスにロードされているモジュールの名前」であり、問題を検知してRaiseException関数を呼び出したりfastfail命令を実行したりせざるを得なくなった被害者である場合がありますので、イベントログに記載されているモジュールだけに着目しないよう注意しましょう。
ダンプファイルが取得できればより詳細な調査が可能ですが、常にダンプファイルを取得するための構成を行っているとは限りませんし、むしろそのような構成としているケースの方が少ないのではないかと思います。関数名を特定してもすぐに原因を特定して問題が解決できるとは限りませんが、エラーが発生した時の状況や再現手順を推測したり、調査方法を検討したりする際の役に立つ場合があります。イベントログの内容もトラブルシュートを進めるための手がかりとなる可能性がありますので是非活用してみましょう。