为啥 Environment.Exit() 不再终止程序?
Posted
技术标签:
【中文标题】为啥 Environment.Exit() 不再终止程序?【英文标题】:Why does Environment.Exit() not terminate the program any more?为什么 Environment.Exit() 不再终止程序? 【发布时间】:2013-08-04 21:40:09 【问题描述】:这是我几天前才发现的,我从this question 得到确认它不仅限于我的机器。
最简单的重现方法是启动一个 Windows 窗体应用程序,添加一个按钮并编写以下代码:
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() 并不能解决此问题。 这发生在 64 位版本的 Windows 8 上,而 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 调用之上没有任何内容,启用了非托管调试器。
【问题讨论】:
我刚刚使用 .NET 4、4 Client Profile、3.5、3.5 Client Profile、3.0 和 2.0 进行了尝试,但没有收到任何错误。我的操作系统是 64 位 Windows 7,使用 VS2010。 @SteveThis happens on the 64-bit version of Windows 8
Hans 这么说!
我可以重现这个(Win 8,64 位),复制/粘贴你的代码并连接一个按钮,我得到了描述的确切症状。
控制台模式应用程序无法演示此问题,当 Exit() 继续发送消息时不会出错。
我之前在一些 64 位 Win7 上遇到过 Exit(0)
的这种行为,现在更改 ExitCode
并没有帮助,现在使用 Process.GetCurrentProcess().Kill()
没有任何问题
【参考方案1】:
我就这个问题与 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 日在我的机器上创建。
结案。
【讨论】:
Windows 更新历史记录在我的机器上不再准确。我只知道它是在 8 月 14 日安装的。【参考方案2】:我不知道为什么它不起作用“不再”,但我认为Environment.Exit
会执行未决的终结器。 Environment.FailFast
没有。
可能是(出于某种奇怪的原因)您有奇怪的未决终结器必须在之后运行,导致这种情况发生。
【讨论】:
您可能正在做某事。终结器正忙于执行 NativeWindow.ForceExitMessageLoop()。奇怪的是它没有嵌套在任何调用中。 @HansPassant:我希望我能重现这个问题,这样我就可以调查它,但我不能。对NativeWindow.ForceExitMessageLoop
的调用是否停留在托管或非托管代码中?它甚至卡住了,还是忙着等待或等待消息或其他什么?
这似乎指向了核心问题。我认为问题的根源在于 IsWindow() winapi 函数。我想我也看到终结器线程上的 2 秒超时,之后一切都陷入困境。调试器没有显示它执行 IsWindow() 调用,但我之前看到 Windows 使用堆栈玩技巧,在 Windows 中输入关键代码时将其切换出去。
我认为 Environment.FailFast() 方法,对于给定的未处理异常情况,无论如何可能是最好的方法。 (我不知道 - 谢谢!)但是有很多遗留代码会使用 Environment.Exit() 不幸的是会尴尬地崩溃:(
你肯定在做某事。就我而言,我已经使用 IHost.StartAsync 启动了一个 IHost 来执行一些集成测试,但是在调用(当然还有等待)IHost.StopAsync 之后,该过程仍然没有终止。只有在调用 IHost.Dispose 之后,进程才会终止。谢谢你的提示【参考方案3】:
这并不能解释为什么会发生这种情况,但我不会在像您的示例这样的按钮事件处理程序中调用 Environment.Exit
- 而是按照 rene's answer 中的建议关闭主窗体。
对于AppDomain.UnhandledException
处理程序,也许您可以只设置Environment.ExitCode
而不是调用Environment.Exit
。
我不确定您要在这里实现什么。为什么要从 Windows 窗体应用程序返回退出代码?通常退出代码由控制台应用程序使用。
我对您可以采取哪些措施来避免这次崩溃特别感兴趣 需要调用 Environment.Exit() 来阻止 WER 对话框显示。
您在 Main 方法中有 try/catch 吗?对于 Windows 窗体应用程序,我总是围绕消息循环以及未处理的异常处理程序进行 try/catch。
【讨论】:
很确定你应该打电话给Application.Exit
而不是Environment.Exit
。
抱歉,这不是解决方法。需要调用 Environment.Exit() 以防止显示 WER 对话框。还要注意“已知事实”,退出代码无关紧要。
@Hans: 是否正在捕获 AppDomain.UnhandledException 以试图避免 WER 对话框的合法性?我的意思是,如果有未处理的异常,WER 对话框应该显示,不是吗?【参考方案4】:
我在我们的应用程序中发现了同样的问题,我们已经通过以下构造解决了它:
Environment.ExitCode=1;
Application.Exit();
【讨论】:
一般来说,Application.Exit()
与 Environment.Exit()
相比,在退出前不会做太多工作,不太可能立即终止应用程序以上是关于为啥 Environment.Exit() 不再终止程序?的主要内容,如果未能解决你的问题,请参考以下文章
System.Environment.Exit(0) 不退出程序
Environment.Exit 和 Main 的简单返回 2 之间的区别
当未使用 Environment.Exit() 时,.net 程序可以具都有哪些退出代码?