如何在程序崩溃时终止程序?(这应该只是单元测试失败而不是永远卡住)

Rom*_*kov 34 language-agnostic unit-testing windows-7

我们的单元测试会触发子进程,有时这些子进程会崩溃.发生这种情况时,会弹出一个Windows错误报告对话框,并且该过程保持活动状态,直到手动关闭该过程.这当然可以防止单元测试终止.

如何避免这种情况?


这是Win7中的一个示例对话框,其中包含常用设置:

替代文字

如果我禁用了AeDebug注册表项,JIT调试选项就会消失:

替代文字

如果我禁用检查解决方案(我似乎只能通过控制面板控制),它看起来像这样,但仍然出现并仍然停止程序死亡直到用户按下某些东西.记录WerAddExcludedApplication也具有此效果.

替代文字

Rom*_*kov 56

jdehaan和Eric Brown的答案摘要,以及这个问题(另见这个问题):

注意这些解决方案也可能会影响其他错误报告,例如无法加载DLL或打开文件.

选项1:全局禁用

在整个用户帐户或机器上全局工作,这既是好处也是缺点.

设置[HKLM|HKCU]\Software\Microsoft\Windows\Windows Error Reporting\DontShowUI为1.更多信息:WER设置.

选项2:禁用应用程序

需要修改崩溃程序,在文档中描述为最佳实践,不适合库函数.

调用SetErrorMode :( SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);或with SEM_FAILCRITICALERRORS).更多信息:禁用程序崩溃对话框(解释奇怪的呼叫安排).

选项2a:禁用功能:

需要修改崩溃程序,需要Windows 7/2008 R2(仅限桌面应用程序)或更高版本,在文档中描述为首选SetErrorMode,适用于线程安全库函数.

调用并重置SetThreadErrorMode:

DWORD OldThreadErrorMode = 0;
SetThreadErrorMode(SEM_FAILCRITICALERRORS,& OldThreadErrorMode);
    …
SetThreadErrorMode (z_OldThreadErrorMode, NULL);
Run Code Online (Sandbox Code Playgroud)

更多信息:没有太多可用的?

选项3:指定处理程序

需要修改崩溃程序.

使用SetUnhandledExceptionFilter来设置简单地退出,可能与报告,并可能在清理尝试自己的结构化异常处理程序.

选项4:捕获作为例外

需要修改崩溃程序.仅适用于.NET应用程序.

将所有代码包装到全局try/catch块中.指定捕获异常的方法HandleProcessCorruptedStateExceptionsAttribute以及可能SecurityCriticalAttribute的方法.更多信息:处理损坏的状态异常

注意:这可能无法捕获托管调试助手导致的崩溃; 如果是这样,这些也需要在应用程序中禁用.

选项5:停止报告流程

在整个用户帐户上全局工作,但仅限于受控制的持续时间.

只要出现以下情况,请终止Windows错误报告流程:

var werKiller = new Thread(() =>
{
    while (true)
    {
        foreach (var proc in Process.GetProcessesByName("WerFault"))
            proc.Kill();
        Thread.Sleep(3000);
    }
});
werKiller.IsBackground = true;
werKiller.Start();
Run Code Online (Sandbox Code Playgroud)

但这仍然不完全是防弹,因为控制台应用程序可能会通过不同的错误消息崩溃,显然由一个名为的内部函数显示NtRaiseHardError:

替代文字

  • "输入点未找到"和"系统错误"("你的计算机中缺少... dll")对话框似乎被```reg add HKLM\SYSTEM\CurrentControlSet\Control\Windows/v ErrorMode/t REG_DWORD/f/d 2```.它们不受DontShowUI注册表设置的影响. (2认同)

jde*_*aan 5

唯一的解决方案是在非常高的级别(对于每个线程)捕获所有异常并正确终止应用程序(或执行另一个操作).

这是防止异常逃离您的应用并激活WER的唯一方法.

加成:

如果除了发生异常之外,您可以AssertNoThrow在另一个单元测试框架中使用(NUnit)或类似的方法来包含触发子进程的代码.通过这种方式,您还可以将其纳入单元测试报告中.在我看来,这是我能想到的最清晰的解决方案.

Addition2:正如下面的评论所示,我错了:你不能总是捕获异步异常,这取决于环境允许的内容.在.NET中,一些例外被阻止被捕获,在这种情况下,什么使我的想法毫无价值......

对于.NET:有一些复杂的解决方法涉及使用AppDomain,导致卸载AppDomain而不是整个应用程序的崩溃.太糟糕了...

http://www.bluebytesoftware.com/blog/PermaLink,guid,223970c3-e1cc-4b09-9d61-99e8c5fae470.aspx

http://www.develop.com/media/pdfs/developments_archive/AppDomains.pdf


编辑:

我终于明白了.使用.NET 4.0您可以将HandleProcessCorruptedStateExceptionsSystem.Runtime.ExceptionServices中的属性添加到包含try/catch块的方法中.这确实有效!也许不推荐但有效.

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.ExceptionServices;

namespace ExceptionCatching
{
    public class Test
    {
        public void StackOverflow()
        {
            StackOverflow();
        }

        public void CustomException()
        {
            throw new Exception();
        }

        public unsafe void AccessViolation()
        {
            byte b = *(byte*)(8762765876);
        }
    }

    class Program
    {
        [HandleProcessCorruptedStateExceptions]
        static void Main(string[] args)
        {
            Test test = new Test();
            try {
                //test.StackOverflow();
                test.AccessViolation();
                //test.CustomException();
            }
            catch
            {
                Console.WriteLine("Caught.");
            }

            Console.WriteLine("End of program");

        }

    }      
}
Run Code Online (Sandbox Code Playgroud)