เหตุใด Environment.Exit () จึงไม่ยุติโปรแกรมอีกต่อไป


135

นี่คือสิ่งที่ฉันค้นพบเมื่อไม่กี่วันที่ผ่านมาฉันได้รับคำยืนยันว่ามันไม่ได้ จำกัด อยู่แค่เครื่องของฉันจากคำถามนี้

วิธีที่ง่ายที่สุดในการทำซ้ำคือเริ่มแอปพลิเคชัน Windows Forms เพิ่มปุ่มและเขียนโค้ดนี้:

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

โปรแกรมล้มเหลวหลังจากคำสั่ง Exit () ดำเนินการ ใน Windows Forms คุณจะได้รับ "ข้อผิดพลาดในการสร้างจุดจับหน้าต่าง"

การเปิดใช้งานการดีบักที่ไม่มีการจัดการทำให้ค่อนข้างชัดเจนว่าเกิดอะไรขึ้น COMห่วงกิริยากำลังดำเนินการและช่วยให้ WM_PAINT ข้อความที่จะส่งมอบ นั่นเป็นอันตรายถึงชีวิตในรูปแบบที่กำจัดทิ้ง

ข้อเท็จจริงเดียวที่ฉันรวบรวมมาคือ:

  • ไม่ได้ จำกัด อยู่แค่การรันกับดีบักเกอร์เท่านั้น สิ่งนี้ล้มเหลวหากไม่มี ค่อนข้างต่ำเช่นกัน WER ผิดพลาดแสดงโต้ตอบขึ้นสองครั้ง
  • มันไม่ได้มีอะไรเกี่ยวข้องกับบิตของกระบวนการ เลเยอร์ wow64 ค่อนข้างมีชื่อเสียง แต่การสร้าง AnyCPU ขัดข้องในลักษณะเดียวกัน
  • ไม่มีส่วนเกี่ยวข้องกับ. NET เวอร์ชัน 4.5 และ 3.5 ขัดข้องในลักษณะเดียวกัน
  • รหัสทางออกไม่สำคัญ
  • การเรียก Thread.Sleep () ก่อนที่จะเรียก Exit () ไม่สามารถแก้ไขได้
  • สิ่งนี้เกิดขึ้นกับ Windows 8 เวอร์ชัน 64 บิตและดูเหมือนว่า Windows 7 จะไม่ได้รับผลกระทบในลักษณะเดียวกัน
  • นี่ควรเป็นพฤติกรรมที่ค่อนข้างใหม่ฉันไม่เคยเห็นมาก่อน ฉันไม่เห็นการอัปเดตที่เกี่ยวข้องที่ส่งผ่านWindows Updateแม้ว่าประวัติการอัปเดตจะไม่ถูกต้องในเครื่องของฉันอีกต่อไป
  • นี่เป็นการทำลายพฤติกรรมอย่างสิ้นเชิง คุณจะเขียนโค้ดเช่นนี้ในตัวจัดการเหตุการณ์สำหรับ AppDomain.UnhandledException และเกิดปัญหาในลักษณะเดียวกัน

ฉันสนใจเป็นพิเศษว่าคุณสามารถทำอะไรได้บ้างเพื่อหลีกเลี่ยงความผิดพลาดนี้ โดยเฉพาะอย่างยิ่งสถานการณ์ AppDomain.UnhandledException ทำให้ฉันสะดุด ไม่มีหลายวิธีในการยุติโปรแกรม. NET โปรดทราบว่าการเรียก Application.Exit () หรือ Form.Close () ไม่ถูกต้องในตัวจัดการเหตุการณ์สำหรับ UnhandledException ดังนั้นจึงไม่ใช่วิธีแก้ปัญหา


อัปเดต: Mehrdad ชี้ให้เห็นว่าเธรด Finalizer อาจเป็นส่วนหนึ่งของปัญหา ฉันคิดว่าฉันเห็นสิ่งนี้และฉันก็เห็นหลักฐานบางอย่างสำหรับการหมดเวลา 2 วินาทีที่ CLR ให้เธรด Finalizer เพื่อดำเนินการให้เสร็จสิ้น

Finalizer อยู่ภายใน NativeWindowForceExitMessageLoop () มีฟังก์ชัน IsWindow () Win32 ที่ตรงกับตำแหน่งรหัสโดยประมาณโดยชดเชย 0x3c เมื่อดูรหัสเครื่องในโหมด 32 บิต ดูเหมือนว่า 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 และไม่ได้รับข้อผิดพลาดใด ๆ Windows 7 64 บิตเป็นระบบปฏิบัติการของฉันโดยใช้ VS2010
Steve

2
@ สตีฟThis happens on the 64-bit version of Windows 8ฮันส์พูดอย่างนั้น!
Parimal Raj

7
ฉันสามารถทำซ้ำได้ (Win 8, 64-bits) คัดลอก / วางรหัสของคุณและต่อสายปุ่มและฉันได้รับอาการที่อธิบายไว้
keyboardP

3
แอปโหมดคอนโซลไม่สามารถแสดงปัญหานี้ได้ไม่มีอะไรผิดพลาดเมื่อออก () ปั๊มข้อความต่อไป
Hans Passant

3
ฉันเคยพบพฤติกรรมExit(0)แบบนี้มาก่อนกับ Win7 64 บิตบางตัวการเปลี่ยนExitCodeไม่ได้ช่วยตอนนี้ใช้งานได้Process.GetCurrentProcess().Kill()โดยไม่มีปัญหาใด ๆ
Sriram Sakthivel

คำตอบ:


87

ฉันติดต่อ Microsoft เกี่ยวกับปัญหานี้และดูเหมือนว่าจะได้ผลแล้ว อย่างน้อยฉันก็อยากจะคิดว่ามันทำได้ :) แม้ว่าฉันจะไม่ได้รับการยืนยันการแก้ปัญหากลับมา แต่กลุ่ม Windows ก็ยากที่จะติดต่อโดยตรงและฉันต้องใช้คนกลาง

การอัปเดตที่ส่งผ่าน Windows Update ช่วยแก้ปัญหาได้ ความล่าช้า 2 วินาทีที่เห็นได้ชัดเจนก่อนที่ความผิดพลาดจะไม่มีอีกต่อไปขอแนะนำอย่างยิ่งว่าการหยุดชะงัก IsWindow () ได้รับการแก้ไขแล้ว และโปรแกรมจะปิดลงอย่างสมบูรณ์และเชื่อถือได้ การอัปเดตที่ติดตั้งโปรแกรมแก้ไขสำหรับ Windows Defender, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll และ wintrust.dll

Uxtheme.dll เป็นสิ่งที่แปลกออกไป ใช้ API ธีม Visual Styles และใช้โดยโปรแกรมทดสอบนี้ ฉันไม่สามารถแน่ใจได้ แต่เงินของฉันอยู่ที่นั่นเป็นต้นตอของปัญหา สำเนาใน C: \ WINDOWS \ system32 มีหมายเลขเวอร์ชัน 6.2.9200.16660 สร้างเมื่อวันที่ 14 สิงหาคม 2013 บนเครื่องของฉัน

ปิดคดี.


12
ประวัติ Windows Update บนเครื่องของฉันไม่ถูกต้องอีกต่อไป ทั้งหมดที่ฉันรู้ก็คือมันถูกติดตั้งเมื่อวันที่ 14 สิงหาคม
Hans Passant

52

ฉันไม่รู้ว่าทำไมมันถึงไม่ทำงาน"อีกต่อไป"แต่ฉันคิดว่าEnvironment.Exitกำลังดำเนินการอยู่ระหว่างการดำเนินการขั้นสุดท้าย Environment.FailFastไม่

อาจเป็นไปได้ว่า (ด้วยเหตุผลที่แปลกประหลาด) คุณมีโปรแกรมเข้ารอบสุดท้ายที่รอดำเนินการอยู่ซึ่งจะต้องดำเนินการในภายหลังทำให้เกิดเหตุการณ์นี้ขึ้น


2
คุณอาจจะทำอะไรบางอย่าง Finalizer กำลังยุ่งอยู่กับการเรียกใช้งาน NativeWindowForceExitMessageLoop () มันไม่ได้ซ้อนอยู่ในการโทรใด ๆ
Hans Passant

@HansPassant: ฉันหวังว่าฉันจะแก้ไขปัญหานี้เพื่อที่ฉันจะได้ตรวจสอบมัน แต่ฉันทำไม่ได้ การโทรNativeWindow.ForceExitMessageLoopติดอยู่ในรหัสที่มีการจัดการหรือไม่มีการจัดการ? มันติดอยู่หรือไม่ว่างรอหรือรอข้อความหรืออย่างอื่น?
user541686

สิ่งนี้ดูเหมือนจะชี้ไปที่ปัญหาหลักอย่างแน่นอน ฉันคิดว่ามันเป็นฟังก์ชัน Winapi IsWindow () ที่เป็นต้นตอของปัญหา ฉันคิดว่าฉันยังเห็นการหมดเวลา 2 วินาทีในเธรด Finalizer หลังจากนั้นทุกอย่างก็ตกนรก ดีบักเกอร์ไม่แสดงว่าเรียกใช้การเรียก IsWindow () แต่ฉันเคยเห็น Windows เล่นเทคนิคกับสแต็กมาก่อนโดยเปลี่ยนออกเมื่อป้อนรหัสสำคัญใน Windows
Hans Passant

4
ฉันคิดว่าวิธี Environment.FailFast () สำหรับกรณีที่ระบุของข้อยกเว้นที่ไม่สามารถจัดการได้น่าจะเป็นวิธีที่ดีที่สุดที่จะใช้ต่อไป (ฉันไม่รู้ - ขอบคุณ!) อย่างไรก็ตามมีโค้ดดั้งเดิมจำนวนมากที่จะใช้ Environment.Exit () ซึ่งจะผิดพลาดอย่างไม่น่าเชื่อ :(
Ian Yates

2
คุณกำลังทำอะไรบางอย่างอย่างแน่นอนที่สุด ในกรณีของฉันฉันได้เริ่มใช้งาน IHost โดยใช้ IHost แล้ว StartAsync เพื่อทำการทดสอบการผสานรวมบางอย่าง แต่หลังจากโทรไป (และแน่นอนว่ารอ) IHost StopAsync กระบวนการยังไม่ยุติ หลังจากโทรหา IHost เท่านั้นทิ้งกระบวนการจะสิ้นสุดลง ขอบคุณสำหรับเคล็ดลับ
Malte R

6

นี้ไม่ได้อธิบายว่าทำไมมันเกิดขึ้น แต่ฉันจะไม่เรียกEnvironment.Exitในตัวจัดการเหตุการณ์ปุ่มเช่นตัวอย่างของคุณ - แทนใกล้รูปแบบหลักเป็นข้อเสนอแนะในคำตอบของ Rene

ในฐานะที่เป็นสำหรับAppDomain.UnhandledExceptionจัดการบางทีคุณก็สามารถตั้งแทนที่จะเรียกEnvironment.ExitCodeEnvironment.Exit

ฉันไม่แน่ใจว่าคุณพยายามทำอะไรที่นี่ เหตุใดคุณจึงต้องการส่งคืนรหัสออกจากแอปพลิเคชัน Windows Forms โดยปกติรหัสทางออกจะถูกใช้โดยแอปพลิเคชันคอนโซล

ฉันสนใจเป็นพิเศษในสิ่งที่คุณสามารถทำได้เพื่อหลีกเลี่ยงความผิดพลาดนี้ Calling Environment จำเป็นต้องออก () เพื่อป้องกันไม่ให้กล่องโต้ตอบ WER แสดง

คุณลอง / จับในวิธีหลักหรือไม่? สำหรับแอปพลิเคชัน Windows Forms ฉันมักจะลอง / จับรอบข้อความตลอดจนตัวจัดการข้อยกเว้นที่ไม่สามารถจัดการได้


ค่อนข้างมั่นใจว่าคุณควรจะเรียกแทนApplication.Exit Environment.Exit
user541686

7
ขออภัยนี่ไม่ใช่วิธีแก้ปัญหาชั่วคราว เรียก Environment.Exit () เพื่อป้องกันไม่ให้กล่องโต้ตอบ WER แสดง โปรดสังเกต "ข้อเท็จจริงที่ทราบ" ด้วยเช่นกันรหัสทางออกไม่สำคัญ
Hans Passant

7
@Hans: กำลังจับ AppDomain.UnhandledException เพื่อพยายามหลีกเลี่ยงกล่องโต้ตอบ WER ที่ถูกต้องตั้งแต่แรก? ฉันหมายความว่าถ้ามีข้อยกเว้นที่ไม่สามารถจัดการได้กล่องโต้ตอบ WER ควรจะแสดงขึ้นใช่ไหม
Harry Johnston

2

ฉันพบปัญหาเดียวกันในแอพของเราเราได้แก้ไขด้วยโครงสร้างต่อไปนี้:

Environment.ExitCode=1;
Application.Exit();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.