Environment.Exit()がプログラムを終了しないのはなぜですか?


134

これはほんの数日前に発見したもので、この質問から私のマシンに限定されないことが確認されました。

これを再現する最も簡単な方法は、Windowsフォームアプリケーションを起動し、ボタンを追加して、次のコードを記述することです。

    private void button1_Click(object sender, EventArgs e) {
        MessageBox.Show("yada");
        Environment.Exit(1);         // Kaboom!
    }

Exit()ステートメントの実行、プログラムは失敗します。Windowsフォームでは、「ウィンドウハンドルの作成エラー」が発生します。

アンマネージデバッグを有効にすると、何が起こっているのかがいくぶん明確になります。COMモーダルループを実行し、WM_PAINTメッセージが配信されることを可能にします。それは処分されたフォームでは致命的です。

これまでに収集した唯一の事実は次のとおりです。

  • デバッガでの実行に限定されません。これも1つもなければ失敗します。どちらかと言えば、WERクラッシュダイアログが2回表示さます。
  • プロセスのビットネスとは何の関係もありません。wow64レイヤーはかなり悪名高いですが、AnyCPUビルドは同じ方法でクラッシュします。
  • .NETバージョンとは何の関係もありません。4.5と3.5は同じようにクラッシュします。
  • 終了コードは関係ありません。
  • Exit()を呼び出す前にThread.Sleep()を呼び出しても修正されません。
  • これは64ビットバージョンのWindows 8で発生し、Windows 7は同じ影響を受けないようです。
  • これは比較的新しい振る舞いであるはずですが、私はこれを見たことはありません。私のマシンでは更新履歴が正確ではなくなっていますがWindows Updateを通じて関連する更新が配信されていません。
  • これはひどく破壊的な動作です。このようなコードをAppDomain.UnhandledExceptionのイベントハンドラーに記述すると、同じようにクラッシュします。

このクラッシュを回避するためにできることには特に興味があります。特に、AppDomain.UnhandledExceptionシナリオは私を困らせます。.NETプログラムを終了する方法は多くありません。Application.Exit()またはForm.Close()の呼び出しはUnhandledExceptionのイベントハンドラーでは無効であるため、回避策ではないことに注意してください。


更新:Mehrdadは、ファイナライザスレッドが問題の一部である可能性があることを指摘しました。私はこれを見ていると思いますし、CLRがファイナライザスレッドに実行を終了させるための2秒のタイムアウトの証拠もいくつか見ています。

ファイナライザはNativeWindow.ForceExitMessageLoop()内にあります。32ビットモードでマシンコードを見ると、コードの場所にほぼ対応するIsWindow()Win32関数があります。オフセットは0x3cです。IsWindow()がデッドロックしているようです。内部の適切なスタックトレースを取得できませんが、デバッガーはP / Invoke呼び出しが返されたと判断します。これは説明するのが難しいです。より良いスタックトレースを取得できる場合は、ぜひご覧ください。私の:

System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.ForceExitMessageLoop() + 0x3c bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Finalize() + 0x16 bytes
[Native to Managed Transition]
kernel32.dll!@BaseThreadInitThunk@12()  + 0xe bytes
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

ForceExitMessageLoop呼び出しを超えるものはなく、アンマネージデバッガーが有効になっています。


2
私はこれを.NET 4、4 Client Profile、3.5、3.5 Client Profile、3.0、および2.0で試したところ、どれもエラーを受け取りませんでした。64ビットWindows 7は私のOSで、VS2010を使用しています。
スティーブ

2
@スティーブThis happens on the 64-bit version of Windows 8・ハンスはそう言った!
Parimal Raj 2013

7
私はこれ(Win 8、64ビット)を再現し、コードをコピーして貼り付け、ボタンを配線すると、説明されているとおりの症状が出ます。
keyboardP

3
コンソールモードアプリはこの問題を実証できませんでした。Exit()がメッセージを送り続けても問題はありません。
Hans Passant 2013

3
私はExit(0)少し前に64ビットWin7でこの種の動作に遭遇しましたが、変更ExitCodeProcess.GetCurrentProcess().Kill()問題なく使用するのに役立ちませんでした
Sriram Sakthivel

回答:


85

この問題についてマイクロソフトに問い合わせたところ、効果があったようです。少なくとも私はそれがそうだったと思いたいです:)。彼らから解決策の確認は得られませんでしたが、Windowsグループに直接連絡することは難しく、仲介者を使用する必要がありました。

Windows Updateを通じて配信されたアップデートで問題が解決しました。クラッシュが発生するまでの2秒の顕著な遅延は、IsWindow()デッドロックが解決されたことを強く示唆しています。そして、プログラムはクリーンかつ確実にシャットダウンします。Windows Defender、wdboot.sys、wdfilter.sys、tcpip.sys、rpcrt4.dll、uxtheme.dll、crypt32.dllおよびwintrust.dll用の更新プログラムがインストールされたパッチ

Uxtheme.dllは奇妙なものです。Visual StylesテーマAPIを実装し、このテストプログラムで使用されます。確かではありませんが、私のお金は問題の原因としてそのお金にあります。C:\ WINDOWS \ system32のコピーのバージョン番号は6.2.9200.16660で、2013年8月14日に私のマシンに作成されました。

ケースは閉じられました。


11
私のマシンでは、Windows Updateの履歴が正確ではなくなりました。私が知っているのは、それが8月14日にインストールされたことです。
ハンスパッサント2013

51

「これ以上」機能しない理由はわかりませんが、Environment.Exit保留中のファイナライザを実行すると思います。Environment.FailFastしません。

(何らかの奇妙な理由により)後で実行する必要のある保留中のファイナライザが奇妙になり、これが発生する可能性があります。


2
あなたは何かをしているかもしれません。ファイナライザはNativeWindow.ForceExitMessageLoop()の実行でビジーです。奇妙なことに、どの呼び出しでもネストされていません。
ハンスパッサント2013

@HansPassant:問題を再現して調査できるようにしたいのですが、できません。呼び出しNativeWindow.ForceExitMessageLoopはマネージコードまたはアンマネージコードで行き詰まっていますか?それは行き詰まっていますか、それとも忙しいのか、メッセージや何かを待っているのですか?
user541686 2013

これは確かに中心的な問題を指摘しているようです。問題の原因となっているのは、IsWindow()winapi関数だと思います。私もファイナライザスレッドで2秒のタイムアウトが発生していると思います。その後、すべてが地獄に行きます。デバッガーはIsWindow()呼び出しの実行を表示しませんが、Windowsが以前にスタックでトリックを再生し、Windows内の重要なコードを入力するときにそれを切り替えることを見てきました。
ハンスパッサント2013

4
未処理の例外の特定のケースでは、Environment.FailFast()メソッドがおそらくとにかく使用するのに最適なメソッドだと思います。(私はそれを認識していませんでした-ありがとう!)ただし、Environment.Exit()を使用するレガシーコードがたくさんあり、残念ながら不自然にクラッシュします:(
Ian Yates

2
あなたは間違いなく何かに夢中になっています。私の場合、IHost.StartAsyncを使用してIHostを開始し、統合テストを実行しましたが、IHost.StopAsyncを呼び出した後(もちろん待機した後)、プロセスは終了しませんでした。IHost.Disposeを呼び出した後にのみ、プロセスは終了します。ヒントをありがとう
Malte R

6

これはなぜ起こっているのかを説明していませんがEnvironment.Exit、サンプルのようなボタンイベントハンドラーを呼び出すことはしません。代わりに、ルネの回答で提案されているようにメインフォームを閉じます

AppDomain.UnhandledExceptionハンドラについてEnvironment.ExitCodeは、を呼び出すのではなく、単に設定することもできますEnvironment.Exit

ここで何を達成しようとしているのかわかりません。Windowsフォームアプリケーションから終了コードを返す必要があるのはなぜですか?通常、終了コードはコンソールアプリケーションで使用されます。

このクラッシュを回避するためにできることには特に興味があります。WERダイアログが表示されないようにするには、Encalling Environment.Exit()が必要です。

Mainメソッドにtry / catchがありますか?Windowsフォームアプリケーションの場合、メッセージループと未処理の例外ハンドラーを常にtry / catchします。


Application.Exit代わりに呼び出すことになっていることを確認してくださいEnvironment.Exit
user541686 2013

7
申し訳ありませんが、これは回避策ではありません。WERダイアログが表示されないようにするには、Environment.Exit()を呼び出す必要があります。「既知の事実」にも注意してください。終了コードは関係ありません。
ハンスパッサント2013

7
@Hans:AppDomain.UnhandledExceptionをキャッチして、最初に正当なWERダイアログを回避しようとしていますか?つまり、未処理の例外がある場合、WERダイアログが表示されるはずですよね。
ハリージョンストン

2

私たちのアプリでも同じ問題が見つかりました。次の構文で解決しました:

Environment.ExitCode=1;
Application.Exit();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.