Mengapa Environment.Exit () tidak menghentikan program lagi?

135

Ini adalah sesuatu yang saya temukan beberapa hari yang lalu, saya mendapat konfirmasi bahwa itu tidak hanya terbatas pada mesin saya dari pertanyaan ini .

Cara termudah untuk melakukan repro adalah dengan memulai aplikasi Windows Forms, tambahkan tombol dan tulis kode ini:

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

Program gagal setelah pernyataan Exit () dijalankan. Pada Formulir Windows Anda mendapatkan "Kesalahan membuat pegangan jendela".

Mengaktifkan debugging yang tidak terkelola membuatnya agak jelas tentang apa yang terjadi. The COM modal loop mengeksekusi dan memungkinkan pesan WM_PAINT yang akan disampaikan. Itu berakibat fatal pada formulir yang dibuang.

Satu-satunya fakta yang saya kumpulkan sejauh ini adalah:

  • Ini tidak hanya terbatas pada menjalankan dengan debugger. Ini juga gagal tanpa satupun. Agak buruk juga, dialog crash WER muncul dua kali .
  • Itu tidak ada hubungannya dengan bitness prosesnya. Lapisan wow64 cukup terkenal, tetapi build AnyCPU mengalami error dengan cara yang sama.
  • Itu tidak ada hubungannya dengan versi .NET, 4.5 dan 3.5 crash dengan cara yang sama.
  • Kode keluar tidak masalah.
  • Memanggil Thread.Sleep () sebelum memanggil Exit () tidak memperbaikinya.
  • Ini terjadi pada versi 64-bit Windows 8, dan Windows 7 tampaknya tidak terpengaruh dengan cara yang sama.
  • Ini seharusnya perilaku yang relatif baru, saya belum pernah melihat ini sebelumnya. Saya tidak melihat pembaruan relevan yang dikirimkan melalui Pembaruan Windows , meskipun riwayat pembaruan tidak lagi akurat di komputer saya.
  • Ini adalah perilaku yang sangat melanggar. Anda akan menulis kode seperti ini di event handler untuk AppDomain.UnhandledException, dan crash dengan cara yang sama.

Saya sangat tertarik pada apa yang mungkin dapat Anda lakukan untuk menghindari kecelakaan ini. Terutama skenario AppDomain.UnhandledException membuat saya bingung; Tidak banyak cara untuk menghentikan program .NET. Harap dicatat bahwa memanggil Application.Exit () atau Form.Close () tidak valid dalam penanganan kejadian untuk UnhandledException, jadi mereka bukan solusi.


PEMBARUAN: Mehrdad menunjukkan bahwa utas finalizer bisa menjadi bagian dari masalah. Saya rasa saya melihat ini dan saya juga melihat beberapa bukti untuk batas waktu 2 detik bahwa CLR memberikan rangkaian finalizer untuk menyelesaikan eksekusi.

Finalizer ada di dalam NativeWindow.ForceExitMessageLoop (). Ada fungsi IsWindow () Win32 di sana yang kira-kira sesuai dengan lokasi kode, offset 0x3c saat melihat kode mesin dalam mode 32-bit. Tampaknya IsWindow () mengalami kebuntuan. Saya tidak bisa mendapatkan pelacakan tumpukan yang baik untuk internal Namun, debugger mengira panggilan P / Invoke baru saja kembali. Ini sulit untuk dijelaskan. Jika Anda bisa mendapatkan jejak tumpukan yang lebih baik maka saya ingin melihatnya. Milikku:

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

Tidak ada di atas panggilan ForceExitMessageLoop, debugger tak terkelola diaktifkan.

Hans Passant
sumber
2
Saya baru saja mencoba ini dengan Profil Klien .NET 4, 4, 3.5, 3.5 Profil Klien, 3.0, dan 2.0, dan tidak menerima kesalahan pada salah satu dari mereka. 64-bit Windows 7 adalah OS saya, menggunakan VS2010.
Steve
2
@Steve This happens on the 64-bit version of Windows 8Hans bilang begitu!
Parimal Raj
7
Saya dapat melakukan repro ini (Win 8, 64-bit), menyalin / menempelkan kode Anda dan menyambungkan tombol dan saya mendapatkan gejala persis seperti yang dijelaskan.
keyboardP
3
Aplikasi mode konsol tidak dapat mendemonstrasikan masalah ini, tidak ada yang salah saat Exit () terus memompa pesan.
Hans Passant
3
Saya telah menemukan perilaku semacam ini Exit(0)dengan beberapa 64bit Win7, Mengubah ExitCodetidak membantu sekarang menggunakan Process.GetCurrentProcess().Kill()tanpa masalah itu berhasil
Sriram Sakthivel

Jawaban:

87

Saya menghubungi Microsoft tentang masalah ini dan tampaknya telah terbayar. Setidaknya saya ingin berpikir begitu :). Meskipun saya tidak mendapatkan konfirmasi resolusi kembali dari mereka, grup Windows sulit untuk dihubungi secara langsung dan saya harus menggunakan perantara.

Pembaruan yang dikirimkan melalui Pembaruan Windows memecahkan masalah. Penundaan 2 detik yang terlihat sebelum kecelakaan tidak lagi ada, sangat menunjukkan bahwa kebuntuan IsWindow () telah diselesaikan. Dan program ditutup dengan rapi dan andal. Pembaruan memasang tambalan untuk Windows Defender, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll dan wintrust.dll

Uxtheme.dll adalah yang aneh-bebek. Ini mengimplementasikan API tema Gaya Visual dan digunakan oleh program uji ini. Saya tidak bisa memastikan, tetapi uang saya ada di sana sebagai sumber masalahnya. Salinan di C: \ WINDOWS \ system32 memiliki nomor versi 6.2.9200.16660, dibuat pada 14 Agustus 2013 di komputer saya.

Kasus ditutup.

Hans Passant
sumber
12
Riwayat Pembaruan Windows tidak lagi akurat di mesin saya. Yang saya tahu adalah bahwa itu diinstal pada 14 Agustus.
Hans Passant
52

Saya tidak tahu mengapa itu tidak berhasil "lagi" , tapi saya pikir Environment.Exitmengeksekusi finalisator yang tertunda. Environment.FailFasttidak.

Mungkin (karena alasan yang aneh) Anda memiliki finalizer aneh yang tertunda yang harus dijalankan setelahnya, yang menyebabkan hal ini terjadi.

pengguna541686
sumber
2
Kamu pasti sedang mengerjakan sesuatu. Finalizer sedang sibuk menjalankan NativeWindow.ForceExitMessageLoop (). Anehnya itu tidak bersarang dalam panggilan apa pun.
Hans Passant
@HansPassant: Saya berharap dapat melaporkan masalah tersebut sehingga saya dapat menyelidikinya, tetapi saya tidak bisa. Apakah panggilan NativeWindow.ForceExitMessageLoopterjebak dalam kode terkelola atau tidak terkelola? Apakah itu macet, atau sibuk menunggu atau menunggu pesan atau hal lain?
pengguna541686
Ini sepertinya menunjuk pada masalah inti. Saya pikir itu adalah fungsi winapi IsWindow () yang menjadi akar masalah. Saya pikir saya juga melihat batas waktu 2 detik pada utas finalizer, setelah itu semuanya masuk ke neraka. Debugger tidak menunjukkannya menjalankan panggilan IsWindow () tetapi saya telah melihat Windows memainkan trik dengan tumpukan sebelumnya, mengubahnya ketika memasukkan kode kritis di dalam Windows.
Hans Passant
4
Saya pikir metode Environment.FailFast (), untuk kasus pengecualian yang tidak tertangani, mungkin adalah metode terbaik untuk digunakan. (Saya tidak menyadarinya - terima kasih!) Namun ada banyak kode warisan yang akan menggunakan Environment.Exit () yang sayangnya akan crash dengan canggung :(
Ian Yates
2
Anda pasti akan melakukan sesuatu. Dalam kasus saya, saya telah memulai IHost menggunakan IHost.StartAsync untuk melakukan beberapa pengujian integrasi, namun setelah memanggil (dan tentu saja menunggu) IHost.StopAsync, prosesnya masih tidak berhenti. Hanya setelah memanggil IHost.Dispose, proses akan berakhir. Terima kasih atas tipnya
Malte R
6

Ini tidak menjelaskan mengapa itu terjadi, tetapi saya tidak akan memanggil Environment.Exitpenangan peristiwa tombol seperti sampel Anda - sebagai gantinya tutup formulir utama seperti yang disarankan dalam jawaban rene .

Sedangkan untuk seorang AppDomain.UnhandledExceptionpawang, mungkin Anda bisa mengatur Environment.ExitCodedaripada menelepon Environment.Exit.

Saya tidak yakin apa yang Anda coba capai di sini. Mengapa Anda ingin mengembalikan kode keluar dari aplikasi Windows Forms? Biasanya kode keluar digunakan oleh aplikasi konsol.

Saya sangat tertarik pada apa yang mungkin dapat Anda lakukan untuk menghindari crash ini Calling Environment. Exit () diperlukan untuk mencegah dialog WER muncul.

Apakah Anda memiliki coba / tangkap dalam metode Utama? Untuk aplikasi Windows Forms, saya selalu mencoba / menangkap sekitar loop pesan serta penangan pengecualian yang tidak tertangani.

Joe
sumber
Cukup yakin Anda seharusnya menelepon, Application.Exitbukan Environment.Exit.
pengguna541686
7
Maaf, ini bukan solusi. Memanggil Environment.Exit () diperlukan untuk mencegah dialog WER muncul. Perhatikan juga "fakta yang diketahui", kode keluar tidak menjadi masalah.
Hans Passant
7
@Hans: menangkap AppDomain.UnhandledException untuk mencoba menghindari dialog WER sah di tempat pertama? Maksud saya, jika ada pengecualian yang tidak tertangani, dialog WER seharusnya ditampilkan, bukan?
Harry Johnston
2

Saya telah menemukan masalah yang sama di aplikasi kami, kami telah menyelesaikannya dengan konstruksi berikut:

Environment.ExitCode=1;
Application.Exit();
Jesse
sumber