Исключение исключений AppDomain.FirstChanceException и стека

Я использую событие 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.");
}

Ответ 1

Это работает для меня:

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;
    }
}

Ответ 2

Связанная с MSDN статья содержит несколько рекомендаций:

Вы должны обрабатывать все исключения, которые происходят в обработчике событий для события FirstChanceException. В противном случае исключение FirstChanceException будет рекурсивно. Это может привести к переполнению стека и завершению работы приложения. Мы рекомендуем внедрять обработчики событий для этого события в качестве областей с ограниченным выполнением (CER), чтобы сохранить связанные с инфраструктурой исключения, такие как переполнение из-за памяти или, воздействовать на виртуальную машину при обработке уведомления об исключении.

Итак, включите свою функцию внутри блока try/catch и вызовите PrepareConstrainedRegion перед блоком, чтобы избежать исключений OutOfMemory: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx

Изменить:. У вас все еще есть проблема с рекурсией даже с блоком try/catch. Итак... Думаю, вам просто нужно вызвать только безопасный код, который не будет вызывать никаких исключений. Этот обработчик событий кажется довольно опасным, я бы рекомендовал использовать его только для целей отладки.

Ответ 3

Сначала используйте метод вместо делегата, поэтому имя метода будет определено

Затем используйте 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
}

Я думаю, что вышеуказанное не будет работать, потому что оно всегда будет содержать имя метода, но вы можете подсчитать, будет ли оно более одного раза. Кроме того, убедитесь, что он не встроен при компиляции в режиме Release, в этом случае у вас проблемы.

Ответ 4

Несмотря на то, что это не очень хорошо, в VB.NET вы можете предотвратить запуск исключения в обработчике событий FirstChanceException с помощью инструкции "On Error Resume Next", исходящей от VB 6. (Я не уверен, что у С# есть что-то подобное)., вы должны предотвратить рекурсию в обработчике событий, как указано здесь. Ниже приведен пример кода, который работает как ожидалось.

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

Ответ 5

В общем случае вы можете обрабатывать, как и все остальные, но, в частности, исключение StackOverflow и OutOfMemory, они не могут обрабатываться в .NET Framework.

Посмотрите здесь: fooobar.com/questions/88025/...

Начиная с .NET Framework версии 2.0, исключение StackOverflowException объект не может быть захвачен блоком try-catch и соответствующим процесс прекращается по умолчанию. Следовательно, пользователям рекомендуется напишите свой код, чтобы обнаружить и предотвратить переполнение стека. Например, если ваше приложение зависит от рекурсии, используйте счетчик или состояние условие для завершения рекурсивного цикла.

Ответ 6

Я думаю, что добавление другого блока 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."); 
} 

Ответ 7

Обработать внутренние исключения вручную, например.

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.");
 }