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


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

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

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

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

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

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

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

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

  1. Определите обработчик событий для события FirstChanceException, используя лямбда-функцию, и присоедините его к событию. В этом примере обработчик событий выводит имя домена приложения, где событие было обработано, а также свойство исключения 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
    
    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);
                };
    
  2. Породите исключение и перехватите его. Еще до того, как среда выполнения найдет обработчик исключений, будет вызвано событие FirstChanceException, которое отобразит сообщение. За ним последует это сообщение, которое отображается обработчиком исключений.

    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
    
    try
    {
        throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName);
    }
    catch (ArgumentException ex)
    {
        Console.WriteLine("ArgumentException caught in {0}: {1}", 
            AppDomain.CurrentDomain.FriendlyName, ex.Message);
    }
    
  3. Вызовите исключение, но не перехватывайте его. Еще до того, как среда выполнения начнет искать обработчик исключений, будет вызвано событие FirstChanceException, которое отобразит сообщение. Обработчика исключений нет, поэтому приложение завершит работу.

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

    Приведенный ниже (в первых трех шагах этой процедуры) код формирует полное консольное приложение. Выходные данные приложения зависят от имени 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 исключения.

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

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

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

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

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

    Dim w As Worker = CType(ad.CreateInstanceAndUnwrap(
                                Assembly.GetExecutingAssembly().FullName, "Worker"),
                            Worker)
    
    Worker w = (Worker) ad.CreateInstanceAndUnwrap(
                            Assembly.GetExecutingAssembly().FullName, "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 ex As ArgumentException
    
        Console.WriteLine("ArgumentException caught in {0}: {1}", 
            AppDomain.CurrentDomain.FriendlyName, ex.Message)
    End Try
    
    // 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);
    }
    
  3. Поместите в метод Thrower код, отвечающий за то, будет ли метод обрабатывать собственное исключение.

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

Пример

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

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

ПримечаниеПримечание

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

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

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(
                                    Assembly.GetExecutingAssembly().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
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(
                                Assembly.GetExecutingAssembly().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
 */

Компиляция кода

  • Этот пример представляет собой приложение командной строки. Для компиляции и запуска этого кода в Visual Studio 2010 добавьте код на C# Console.ReadLine(); (в Visual Basic — Console.ReadLine()) в конец метода Main(), чтобы предотвратить закрытие командного окна до того, как вы успеете прочитать выходные данные.

См. также

Ссылки

FirstChanceException