【发布时间】: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。
-
@Steve
This happens on the 64-bit version of Windows 8Hans 这么说! -
我可以重现这个(Win 8,64 位),复制/粘贴你的代码并连接一个按钮,我得到了描述的确切症状。
-
控制台模式应用程序无法演示此问题,当 Exit() 继续发送消息时不会出错。
-
我之前在一些 64 位 Win7 上遇到过
Exit(0)的这种行为,现在更改ExitCode并没有帮助,现在使用Process.GetCurrentProcess().Kill()没有任何问题