144

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

重现它的最简单方法是启动 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 调用之上没有任何内容,启用了非托管调试器。

4

4 回答 4

90

我就这个问题联系了微软,这似乎得到了回报。至少我想认为它做到了:)。虽然我没有从他们那里得到解决方案的确认,但 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 日在我的机器上创建。

结案。

于 2013-08-17T13:51:10.540 回答
54

我不知道为什么它“不再”工作,但我认为Environment.Exit执行挂起的终结器。Environment.FailFast没有。

可能是(出于某种奇怪的原因)你有奇怪的待定终结器,必须在之后运行,导致这种情况发生。

于 2013-08-03T20:55:45.620 回答
6

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

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

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

我对您可以采取哪些措施来避免这种崩溃特别感兴趣,需要调用 Environment.Exit() 来防止显示 WER 对话框。

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

于 2013-08-03T20:42:09.620 回答
4

我在我们的应用程序中发现了同样的问题,我们使用以下构造解决了它:

Environment.ExitCode=1;
Application.Exit();
于 2018-04-11T08:03:32.537 回答