Поделиться через


Практическое руководство. Получение уведомлений о первом этапе обработки исключений

Примечание.

Эта статья относится к .NET Framework. Он не применяется к более новым реализациям .NET, включая .NET 6 и более поздние версии.

Событие FirstChanceException класса AppDomain позволяет получать уведомления о порождении исключений до того, как среда CLR начнет искать обработчики исключений.

Событие возникает на уровне домена приложения. Поток выполнения может проходить несколько доменов приложения, поэтому необработанное в одном домене исключение может быть обработано в другом домене. Уведомление происходит во всех доменах приложения, добавивших обработчик для этого события, пока исключение не будет обработано.

Процедуры и примеры в этой статье демонстрируют, как можно получать уведомления о первичных исключениях в рамках простой программы с одним доменом приложения, а также в созданном пользователем домене приложения.

Более развернутый пример с несколькими доменами приложения см. в описании события FirstChanceException.

Получение уведомлений о первом этапе обработки исключений в домене приложения по умолчанию

В представленной ниже процедуре точка входа для приложения, метод Main(), запускается в домене приложения по умолчанию.

Получение уведомлений о первом этапе обработки исключений в домене приложения по умолчанию

  1. Определите обработчик событий для события FirstChanceException, используя лямбда-функцию, и присоедините его к событию. В этом примере обработчик событий выводит имя домена приложения, где событие было обработано, а также свойство исключения Message.

    using System;
    using System.Runtime.ExceptionServices;
    
    class Example
    {
        static void Main()
        {
            AppDomain.CurrentDomain.FirstChanceException +=
                (object source, FirstChanceExceptionEventArgs e) =>
                {
                    Console.WriteLine("FirstChanceException event raised in {0}: {1}",
                        AppDomain.CurrentDomain.FriendlyName, e.Exception.Message);
                };
    
    Imports System.Runtime.ExceptionServices
    
    Class Example
    
        Shared Sub Main()
    
            AddHandler AppDomain.CurrentDomain.FirstChanceException,
                       Sub(source As Object, e As FirstChanceExceptionEventArgs)
                           Console.WriteLine("FirstChanceException event raised in {0}: {1}",
                                             AppDomain.CurrentDomain.FriendlyName,
                                             e.Exception.Message)
                       End Sub
    
  2. Вызовите исключение и перехватите его. Еще до того, как среда выполнения найдет обработчик исключений, будет вызвано событие FirstChanceException, которое отобразит сообщение. За ним последует это сообщение, которое отображается обработчиком исключений.

    try
    {
        throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
    }
    catch (ArgumentException ex)
    {
        Console.WriteLine("ArgumentException caught in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, ex.Message);
    }
    
    Try
        Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName)
    
    Catch ex As ArgumentException
    
        Console.WriteLine("ArgumentException caught in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, ex.Message)
    End Try
    
  3. Вызовите исключение, но не перехватывайте его. Еще до того, как среда выполнения найдет обработчик исключений, будет вызвано событие FirstChanceException, которое отобразит сообщение. Обработчика исключений нет, поэтому приложение завершит работу.

            throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
        }
    }
    
            Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName)
        End Sub
    End Class
    

    Приведенный ниже (в первых трех шагах этой процедуры) код формирует полное консольное приложение. Выходные данные приложения зависят от имени EXE-файла, потому что имя домена приложения по умолчанию состоит из имени и расширения EXE-файла. В следующем примере показаны выходные данные.

    /* This example produces output similar to the following:
    
    FirstChanceException event raised in Example.exe: Thrown in Example.exe
    ArgumentException caught in Example.exe: Thrown in Example.exe
    FirstChanceException event raised in Example.exe: Thrown in Example.exe
    
    Unhandled Exception: System.ArgumentException: Thrown in Example.exe
       at Example.Main()
     */
    
    ' This example produces output similar to the following:
    '
    'FirstChanceException event raised in Example.exe: Thrown in Example.exe
    'ArgumentException caught in Example.exe: Thrown in Example.exe
    'FirstChanceException event raised in Example.exe: Thrown in Example.exe
    '
    'Unhandled Exception: System.ArgumentException: Thrown in Example.exe
    '   at Example.Main()
    

Получение уведомлений о первом этапе обработки исключений в другом домене приложения

Если в программе имеется несколько доменов приложения, можно выбирать, какие из них должны получать уведомления.

Получение уведомлений о первом этапе обработки исключений в созданном пользователем домене приложения

  1. Определите обработчик событий для события FirstChanceException. В этом примере используется метод static (Shared в Visual Basic), выводящий имя домена приложения, обработавшего исключение, и свойство Message исключения.

    static void FirstChanceHandler(object source, FirstChanceExceptionEventArgs e)
    {
        Console.WriteLine("FirstChanceException event raised in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, e.Exception.Message);
    }
    
    Shared Sub FirstChanceHandler(ByVal source As Object,
                                  ByVal e As FirstChanceExceptionEventArgs)
    
        Console.WriteLine("FirstChanceException event raised in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, e.Exception.Message)
    End Sub
    
  2. Создайте домен приложения и добавьте обработчик событий FirstChanceException для этого домена приложения. В этом примере домен приложения называется AD1.

    AppDomain ad = AppDomain.CreateDomain("AD1");
    ad.FirstChanceException += FirstChanceHandler;
    
    Dim ad As AppDomain = AppDomain.CreateDomain("AD1")
    AddHandler ad.FirstChanceException, AddressOf FirstChanceHandler
    

    Данное событие можно аналогичным образом обработать в домене приложения по умолчанию. Используйте свойство static (Shared в Visual Basic) с модификатором AppDomain.CurrentDomain в методе Main() для получения ссылки на домен приложения по умолчанию.

Демонстрация уведомлений о первом этапе обработки исключений в домене приложения

  1. Создайте объект Worker в домене приложения, созданном в предыдущей процедуре. Класс Worker должен быть открытым наследником MarshalByRefObject, как показано в полном примере в конце статьи.

    Worker w = (Worker) ad.CreateInstanceAndUnwrap(
                            typeof(Worker).Assembly.FullName, "Worker");
    
    Dim w As Worker = CType(ad.CreateInstanceAndUnwrap(
                                GetType(Worker).Assembly.FullName, "Worker"),
                            Worker)
    
  2. Вызовите метод объекта Worker, который создает исключение. В этом примере метод Thrower вызывается дважды. В первый раз аргумент метода равен true, в результате чего метод перехватывает собственное исключение самостоятельно. Во второй раз аргумент равен false, и метод Main() перехватывает исключение в домене приложения по умолчанию.

    // The worker throws an exception and catches it.
    w.Thrower(true);
    
    try
    {
        // The worker throws an exception and doesn't catch it.
        w.Thrower(false);
    }
    catch (ArgumentException ex)
    {
        Console.WriteLine("ArgumentException caught in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, ex.Message);
    }
    
    ' The worker throws an exception and catches it.
    w.Thrower(true)
    
    Try
        ' The worker throws an exception and doesn't catch it.
        w.Thrower(false)
    
    Catch ex As ArgumentException
    
        Console.WriteLine("ArgumentException caught in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, ex.Message)
    End Try
    
  3. Поместите в метод Thrower код, отвечающий за то, будет ли метод обрабатывать собственное исключение.

    if (catchException)
    {
        try
        {
            throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine("ArgumentException caught in {0}: {1}",
                AppDomain.CurrentDomain.FriendlyName, ex.Message);
        }
    }
    else
    {
        throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
    }
    
    If catchException
    
        Try
            Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName)
    
        Catch ex As ArgumentException
    
            Console.WriteLine("ArgumentException caught in {0}: {1}",
                AppDomain.CurrentDomain.FriendlyName, ex.Message)
        End Try
    Else
    
        Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName)
    End If
    

Пример

В следующем примере создается домен приложения AD1, после чего для события FirstChanceException этого домена добавляется обработчик событий. Затем в этом домене приложения создается экземпляр класса Worker и вызывается метод Thrower, порождающий исключение ArgumentException. В зависимости от значения аргумента метод либо перехватит исключение, либо не сможет его обработать.

Каждый раз Thrower метод вызывает исключение в AD1, FirstChanceException события в AD1, и обработчик событий выводит сообщение. Затем среда выполнения ищет обработчик исключений. В первом случае он обнаруживается в AD1. Во втором случае исключение в AD1 не обрабатывается и перехватывается только в домене приложения по умолчанию.

Примечание.

Имя домена приложения по умолчанию совпадает с именем исполняемого файла.

При добавлении обработчика для события FirstChanceException в домен приложения по умолчанию событие возникает и обрабатывается еще до того, как домен приложения по умолчанию обработает исключение. Чтобы увидеть, как это происходит, добавьте код на C# AppDomain.CurrentDomain.FirstChanceException += FirstChanceException; (в Visual Basic — AddHandler AppDomain.CurrentDomain.FirstChanceException, FirstChanceException) в начало метода Main().

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

class Example
{
    static void Main()
    {
        // To receive first chance notifications of exceptions in
        // an application domain, handle the FirstChanceException
        // event in that application domain.
        AppDomain ad = AppDomain.CreateDomain("AD1");
        ad.FirstChanceException += FirstChanceHandler;

        // Create a worker object in the application domain.
        Worker w = (Worker) ad.CreateInstanceAndUnwrap(
                                typeof(Worker).Assembly.FullName, "Worker");

        // The worker throws an exception and catches it.
        w.Thrower(true);

        try
        {
            // The worker throws an exception and doesn't catch it.
            w.Thrower(false);
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine("ArgumentException caught in {0}: {1}",
                AppDomain.CurrentDomain.FriendlyName, ex.Message);
        }
    }

    static void FirstChanceHandler(object source, FirstChanceExceptionEventArgs e)
    {
        Console.WriteLine("FirstChanceException event raised in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, e.Exception.Message);
    }
}

public class Worker : MarshalByRefObject
{
    public void Thrower(bool catchException)
    {
        if (catchException)
        {
            try
            {
                throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine("ArgumentException caught in {0}: {1}",
                    AppDomain.CurrentDomain.FriendlyName, ex.Message);
            }
        }
        else
        {
            throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
        }
    }
}

/* This example produces output similar to the following:

FirstChanceException event raised in AD1: Thrown in AD1
ArgumentException caught in AD1: Thrown in AD1
FirstChanceException event raised in AD1: Thrown in AD1
ArgumentException caught in Example.exe: Thrown in AD1
 */
Imports System.Reflection
Imports System.Runtime.ExceptionServices

Class Example
    Shared Sub Main()

        ' To receive first chance notifications of exceptions in 
        ' an application domain, handle the FirstChanceException
        ' event in that application domain.
        Dim ad As AppDomain = AppDomain.CreateDomain("AD1")
        AddHandler ad.FirstChanceException, AddressOf FirstChanceHandler


        ' Create a worker object in the application domain.
        Dim w As Worker = CType(ad.CreateInstanceAndUnwrap(
                                    GetType(Worker).Assembly.FullName, "Worker"),
                                Worker)

        ' The worker throws an exception and catches it.
        w.Thrower(true)

        Try
            ' The worker throws an exception and doesn't catch it.
            w.Thrower(false)

        Catch ex As ArgumentException

            Console.WriteLine("ArgumentException caught in {0}: {1}",
                AppDomain.CurrentDomain.FriendlyName, ex.Message)
        End Try
    End Sub

    Shared Sub FirstChanceHandler(ByVal source As Object,
                                  ByVal e As FirstChanceExceptionEventArgs)

        Console.WriteLine("FirstChanceException event raised in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, e.Exception.Message)
    End Sub
End Class

Public Class Worker
    Inherits MarshalByRefObject

    Public Sub Thrower(ByVal catchException As Boolean)

        If catchException

            Try
                Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName)

            Catch ex As ArgumentException

                Console.WriteLine("ArgumentException caught in {0}: {1}",
                    AppDomain.CurrentDomain.FriendlyName, ex.Message)
            End Try
        Else

            Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName)
        End If
    End Sub
End Class

' This example produces output similar to the following:
'
'FirstChanceException event raised in AD1: Thrown in AD1
'ArgumentException caught in AD1: Thrown in AD1
'FirstChanceException event raised in AD1: Thrown in AD1
'ArgumentException caught in Example.exe: Thrown in AD1

См. также