AppDomain.FirstChanceException和堆栈溢出异常

时间:2012-05-22 06:48:49

标签: c#

我正在使用FirstChanceException事件来记录有关任何抛出异常的详细信息。

static void Main(string[] args)
{
    AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
    {
        Console.WriteLine("Inside first chance exception.");
    };

    throw new Exception("Exception thrown in main.");
}

这可以按预期工作。但是如果在事件处理程序中抛出异常,则会发生堆栈溢出,因为事件将以递归方式引发。

static void Main(string[] args)
{
    AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
    {
        throw new Exception("Stackoverflow");
    };

    throw new Exception("Exception thrown in main.");
}

如何处理事件处理程序中发生的异常?

编辑:

有一些答案表明我将代码包装在try / catch块中的事件处理程序中,但是这不起作用,因为事件是在可以处理异常之前引发的。

static void Main(string[] args)
{
    AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
    {
        try
        {
            throw new Exception("Stackoverflow");
        }
        catch
        {
        }
    };

    throw new Exception("Exception thrown in main.");
}

7 个答案:

答案 0 :(得分:9)

这对我有用:

private volatile bool _insideFirstChanceExceptionHandler;    

// ...

AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;

// ...

private void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs args)
{
    if (_insideFirstChanceExceptionHandler)
    {
        // Prevent recursion if an exception is thrown inside this method
        return;
    }

    _insideFirstChanceExceptionHandler = true;
    try
    {
        // Code which may throw an exception
    }
    catch
    {
        // You have to catch all exceptions inside this method
    }
    finally
    {
        _insideFirstChanceExceptionHandler = false;
    }
}

答案 1 :(得分:1)

虽然这不是一个好方法,但是在VB .NET中,您可以使用“On Error Resume Next”语句来防止在FirstChanceException事件处理程序中触发异常,来自VB 6.(我不确定C#是否有类似的东西)另外,你应该像前面提到的here一样阻止对事件处理程序的递归。以下是示例代码,似乎按预期工作。

Sub Main(args As String())
    AddHandler AppDomain.CurrentDomain.FirstChanceException, AddressOf FirstChanceExceptionEventHandler
    Throw New Exception("Exception thrown in main.")
End Sub

Private Sub FirstChanceExceptionEventHandler(ByVal source As Object, ByVal e As FirstChanceExceptionEventArgs)
    On Error Resume Next

    Dim frames As StackFrame() = New StackTrace(1).GetFrames()
    Dim currentMethod As MethodBase = MethodBase.GetCurrentMethod()
    If frames IsNot Nothing AndAlso frames.Any(Function(x) x.GetMethod() = currentMethod) Then
        Return
    Else
        Throw New Exception("Stackoverflow")
    End If
End Sub

答案 2 :(得分:0)

您链接的MSDN文章提出了一些建议:

  

您必须处理FirstChanceException事件的事件处理程序中发生的所有异常。否则,递归引发FirstChanceException。这可能导致堆栈溢出和应用程序终止。我们建议您将此事件的事件处理程序实现为受约束的执行区域(CER),以便在处理异常通知时保持与内存相关的异常(例如内存不足或堆栈溢出)不会影响虚拟机。

所以将你的函数包含在try / catch块中,并在块之前调用PrepareConstrainedRegion以避免OutOfMemory异常:http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx

编辑:嗯,即使使用try / catch块,你仍然会遇到递归问题。所以...我想你只需要调用不会抛出任何异常的安全代码。该事件处理程序似乎非常危险,我建议仅将其用于调试目的。

答案 3 :(得分:0)

首先使用方法而不是委托,因此将定义方法名称

然后使用Environment.StackTrace检查该方法是否已经在stacktrace

以下是一段代码未经测试

static void Main(string[] args) 
{ 
    AppDomain.CurrentDomain.FirstChanceException += handleFirstChanceException;
} 

private void handleFirstChanceException(object sender, EventArgs eventArgs)
{
    if (Environment.StackTrace.Contains("handleFirstChanceException"))
        return;

    // handle
}

我认为上述操作无效,因为它始终包含方法名称,但如果它出现的次数超过1次,则可以计算。 另外,在Release模式下编译时检查它是否没有内联,在这种情况下你遇到了麻烦

答案 4 :(得分:-1)

一般情况下,您可以像处理所有其他异常一样处理异常,但在特定的StackOverflowOutOfMemory异常中,它们无法在.NET Framework中处理。

请看这里:How do I prevent and/or handle a StackOverflowException? (C#)

  

从.NET Framework 2.0版开始,发生StackOverflowException   对象不能被try-catch块和相应的块捕获   进程默认终止。因此,建议用户   编写代码来检测并防止堆栈溢出。例如,   如果您的应用程序依赖于递归,请使用计数器或状态   条件终止递归循环。

答案 5 :(得分:-1)

我认为在异常处理程序中添加另一个try {} catch (){}块会有所帮助

static void Main(string[] args) 
{ 
    AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) => 
    { 
        try {
            throw new Exception("Stackoverflow"); 
        } catch (Exception e)
        {
           // Do something very simple not throwing an exception...
        }
    }; 

    throw new Exception("Exception thrown in main."); 
} 

答案 6 :(得分:-1)

手动处理例外情况,例如

static void Main(string[] args) {
     AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
     {
         try{
         throw new Exception("Stackoverflow");} catch (Exception ex){/*manual handle*/}
     };
      throw new Exception("Exception thrown in main.");
 }