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

Han*_*ant 133 .net c# windows

这是我发现的短短数天前,我得到了确认,这并不仅仅局限于我的机器从这个问题.

重新编写它的最简单方法是启动Windows窗体应用程序,添加一个按钮并编写以下代码:

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

执行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
Run Code Online (Sandbox Code Playgroud)

ForceExitMessageLoop调用之上没有任何内容,启用了非托管调试器.

Han*_*ant 82

我联系了微软有关这个问题,这似乎已经得到了回报.至少我想它确实:).虽然我没有得到他们的回复确认,但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 Update历史记录不再准确.我所知道的是它已于8月14日安装完毕. (10认同)
  • 你知道哪个更新解决了这个问题吗? (3认同)

Meh*_*dad 50

我不知道为什么它不工作"更多",但我认为Environment.Exit执行挂起的终结.Environment.FailFast没有.

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

  • 我认为对于给定的未处理异常情况,Environment.FailFast()方法可能是最好的方法.(我不知道它 - 谢谢!)然而,有很多遗留代码将使用Environment.Exit(),不幸的是会崩溃:( (4认同)
  • 您可能正在做某事。终结器正在忙于执行NativeWindow.ForceExitMessageLoop()。奇怪的是它没有嵌套在任何调用中。 (2认同)
  • 你绝对是在做某事。就我而言,我使用 IHost.StartAsync 启动了 IHost 来执行一些集成测试,但在调用(当然还有等待)IHost.StopAsync 后,该进程仍然没有终止。仅在调用 IHost.Dispose 后,进程才会终止。谢谢你的提示 (2认同)

Joe*_*Joe 6

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

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

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

我特别感兴趣的是你可以做些什么来避免这种崩溃调用Environment.Exit()是防止WER对话框显示所必需的.

你在Main方法中有try/catch吗?对于Windows窗体应用程序,我总是在消息循环周围有一个try/catch以及未处理的异常处理程序.

  • 对不起,这不是一种解决方法.调用Environment.Exit()是为了防止显示WER对话框.注意"已知事实",退出代码无关紧要. (7认同)
  • @Hans:正在捕捉AppDomain.UnhandledException以试图首先避免WER对话合法吗?我的意思是,如果有一个未处理的异常,WER对话框*应该*显示,不是吗? (7认同)