web-dev-qa-db-ja.com

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

これはほんの数日前に発見したもので、 この質問 から自分のマシンだけに限定されないという確認を得ました。

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

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

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

アンマネージデバッグを有効にすると、何が起こっているかがある程度明確になります。 [〜#〜] com [〜#〜] モーダルループが実行されており、WM_Paintメッセージを配信できます。廃棄されたフォームでは致命的です。

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

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

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


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

ファイナライザーはNativeWindow.ForceExitMessageLoop()内にあります。コードの場所にほぼ対応するIsWindow()Win32関数があり、32ビットモードでマシンコードを見るとオフセット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呼び出しの上には何もありません。アンマネージデバッガーは有効です。

133
Hans Passant

この問題についてマイクロソフトに問い合わせたところ、報われたようです。少なくとも私はそれがやったと思いたい:)。私は彼らから解決策の確認を得られなかったが、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日に私のマシンで作成されました。

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

82
Hans Passant

なぜ機能しないのかわかりません"any more"ですが、Environment.Exit保留中のファイナライザーを実行します。 Environment.FailFastしません。

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

50
Mehrdad

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

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

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

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

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

6
Joe

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

Environment.ExitCode=1;
Application.Exit();
1
user9629169