为什么Environment.Exit()不再终止程序?


134

这是几天前我发现的,从这个问题中我得到的确认不仅仅限于我的机器。

进行复制的最简单方法是启动Windows Forms应用程序,添加按钮并编写以下代码:

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

Exit()语句执行,程序失败。在Windows窗体上,您会收到“错误创建窗口句柄”。

启用非托管调试可以使事情更加清晰。在COM模式循环正在执行,并允许待递送WM_PAINT消息。这对处置形式是致命的。

到目前为止,我收集到的唯一事实是:

  • 它不仅限于与调试器一起运行。没有一个,这也会失败。同样糟糕的是,WER崩溃对话框显示了两次
  • 它与过程的复杂性无关。wow64层是非常臭名昭著的,但是AnyCPU构建会以相同的方式崩溃。
  • 它与.NET版本无关,4.5和3.5崩溃的方式相同。
  • 退出代码无关紧要。
  • 在调用Exit()之前调用Thread.Sleep()不能解决该问题。
  • 在Windows 8的64位版本上会发生这种情况,并且Windows 7似乎不会以相同的方式受到影响。
  • 这应该是相对较新的行为,我之前从未见过。我没有看到通过Windows Update交付的相关更新,尽管更新历史记录在我的计算机上不再准确。
  • 这是严重破坏行为。您将在AppDomain.UnhandledException的事件处理程序中编写这样的代码,并且崩溃的方式相同。

我对您可以采取什么措施来避免此崩溃特别感兴趣。特别是AppDomain.UnhandledException场景使我感到困惑;没有很多方法来终止.NET程序。请注意,在事件处理程序中对UnhandledException调用Application.Exit()或Form.Close()无效,因此它们不是解决方法。


更新:Mehrdad指出终结器线程可能是问题的一部分。我想我已经看到了,也看到了2秒超时的一些证据,表明CLR提供了终结器线程来完成执行。

终结器位于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调用上方没有任何内容,已启用非托管调试器。


2
我只是使用.NET 4、4客户端配置文件,3.5、3.5客户端配置文件,3.0和2.0进行了尝试,但没有收到任何错误。我的操作系统是64位Windows 7,使用VS2010。
史蒂夫·

2
@史蒂夫This happens on the 64-bit version of Windows 8·汉斯这样说!
Parimal Raj

7
我可以对此进行复制(Win 8,64位),复制/粘贴您的代码并连接一个按钮,然后我得到确切的症状描述。
keyboardP

3
控制台模式的应用程序无法演示此问题,当Exit()继续发送消息时,不会出现任何错误。
汉斯·帕桑

3
我在Exit(0)64位Win7上遇到过这种行为,“更改” ExitCode现在Process.GetCurrentProcess().Kill()
无济于事,

Answers:


85

我就此问题联系了Microsoft,这似乎已经得到了回报。至少我想这样做:)。尽管我没有从他们那里得到解决方案的确认,但是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日安装。
汉斯·帕桑

51

我不知道为什么它“不再起作用,但我认为Environment.Exit执行待定的终结器。Environment.FailFast不。

(出于某种奇怪的原因)可能是因为您有怪异的未决终结器必须随后运行,从而导致这种情况的发生。


2
您可能正在做某事。终结器正在忙于执行NativeWindow.ForceExitMessageLoop()。奇怪的是它没有嵌套在任何调用中。
汉斯·帕桑

@HansPassant:我希望我可以重现该问题,以便调查一下,但我不能。调用是否NativeWindow.ForceExitMessageLoop卡在托管或非托管代码中?它甚至卡住了,还是在忙于等待或等待消息或其他?
2013年

这显然是核心问题。我认为问题的根源在于IsWindow()winapi函数。我想我也在finalizer线程上看到了2秒超时,此后一切都变得很糟。调试器没有显示它正在执行IsWindow()调用,但是我之前已经看到Windows在堆栈上发挥了作用,在Windows中输入关键代码时将其切换出来。
汉斯·帕桑

4
对于给定的未处理异常,我认为Environment.FailFast()方法可能是最好的使用方法。(我没有意识到-谢谢!)但是,有很多遗留代码将使用Environment.Exit()不幸地会崩溃:(
Ian Yates

2
您绝对可以做某事。就我而言,我已经使用IHost.StartAsync启动了一个IHost来执行一些集成测试,但是在调用(当然要等待)IHost.StopAsync之后,该过程仍然没有终止。仅在调用IHost.Dispose之后,该过程才会终止。谢谢你的小费
马尔特- [R

6

这并不能解释为什么会发生这种情况,但是我不会Environment.Exit像您的示例那样调用按钮事件处理程序-而是按照rene的答案中的建议关闭主窗体。

至于AppDomain.UnhandledException处理程序,也许您可​​以设置Environment.ExitCode而不是调用Environment.Exit

我不确定您要在这里实现什么。为什么要从Windows Forms应用程序返回退出代码?通常,退出代码由控制台应用程序使用。

我对如何避免此崩溃特别感兴趣,需要调用Calling Environment.Exit()来防止显示WER对话框。

您是否在Main方法中有try / catch?对于Windows Forms应用程序,我总是在消息循环以及未处理的异常处理程序周围进行尝试/捕获。


非常确定您应该打电话给Application.Exit而不是Environment.Exit
2013年

7
抱歉,这不是解决方法。需要调用Environment.Exit()以防止显示WER对话框。还要注意“已知事实”,退出代码无关紧要。
汉斯·帕桑

7
@Hans:正在捕获AppDomain.UnhandledException试图首先避免WER对话框合法吗?我的意思是,如果有未处理的异常,则应该显示WER对话框,不是吗?
哈里·约翰斯顿

2

我在我们的应用中发现了同样的问题,我们使用以下结构解决了该问题:

Environment.ExitCode=1;
Application.Exit();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.