Procédure : recevoir des notifications des exceptions de première chance

L’événement FirstChanceException de la classe AppDomain vous permet de recevoir la notification qu’une exception a été levée, avant que le Common Language Runtime n’ait commencé à rechercher des gestionnaires d’exceptions.

L’événement est déclenché au niveau du domaine d’application. Étant donné qu’un thread d’exécution peut traverser plusieurs domaines d’application, une exception qui n’est pas gérée dans un domaine d’application pourrait être gérée dans un autre domaine d’application. La notification se produit dans chaque domaine d’application qui a ajouté un gestionnaire pour l’événement, jusqu’à ce qu’un domaine d’application gère l’exception.

Les procédures et exemples dans cet article indiquent comment recevoir des notifications des exceptions de première chance dans un programme simple qui a un domaine d’application, et dans un domaine d’application que vous créez.

Pour obtenir un exemple plus complexe qui couvre plusieurs domaines d’application, consultez l’exemple de l’événement FirstChanceException.

Réception de notifications des exceptions de première chance dans le domaine d’application par défaut

Dans la procédure suivante, le point d’entrée de l’application (la méthode Main()) s’exécute dans le domaine d’application par défaut.

Pour illustrer les notifications des exceptions de première chance dans le domaine d’application par défaut

  1. Définissez un gestionnaire d’événements pour l’événement FirstChanceException, à l’aide d’une fonction lambda, et attachez-le à l’événement. Dans cet exemple, le gestionnaire d’événements imprime le nom du domaine d’application où l’événement a été géré, ainsi que la propriété Message de l’exception.

    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. Levez une exception et interceptez-la. Avant que le runtime ne trouve le gestionnaire d’exceptions, l’événement FirstChanceException est déclenché et affiche un message. Ce message est suivi du message affiché par le gestionnaire d’exceptions.

    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. Levez une exception mais ne l’interceptez pas. Avant que le runtime ne recherche le gestionnaire d’exceptions, l’événement FirstChanceException est déclenché et affiche un message. Comme il n’y a aucun gestionnaire d’exceptions, l’application s’arrête.

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

    Le code affiché aux trois premières étapes de cette procédure forme une application console complète. La sortie de l’application varie en fonction du nom du fichier .exe, car le nom du domaine d’application par défaut se compose du nom et de l’extension du fichier .exe. Vous trouverez ci-dessous un exemple de sortie.

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

Réception de notifications des exceptions de première chance dans un autre domaine d’application

Si votre programme contient plusieurs domaines d’application, vous pouvez choisir ceux qui reçoivent des notifications.

Pour recevoir des notifications des exceptions de première chance dans un domaine d’application que vous créez

  1. Définissez un gestionnaire d’événements pour l’événement FirstChanceException. Cet exemple utilise une méthode static (méthode Shared en Visual Basic) qui imprime le nom du domaine d’application où l’événement a été géré, ainsi que la propriété Message de l’exception.

    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. Créez un domaine d’application et ajoutez le gestionnaire d’événements à l’événement FirstChanceException pour ce domaine d’application. Dans cet exemple, le domaine d’application se nomme AD1.

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

    Vous pouvez gérer cet événement dans le domaine d’application par défaut de la même façon. Utilisez la propriété static (Shared en Visual Basic) AppDomain.CurrentDomain dans Main() pour obtenir une référence au domaine d’application par défaut.

Pour illustrer les notifications des exceptions de première chance dans le domaine d’application

  1. Créez un objet Worker dans le domaine d’application que vous avez créé lors de la procédure précédente. La classe Worker doit être publique et doit dériver de MarshalByRefObject, comme indiqué dans l’exemple complet à la fin de cet article.

    Worker w = (Worker) ad.CreateInstanceAndUnwrap(
                            typeof(Worker).Assembly.FullName, "Worker");
    
    Dim w As Worker = CType(ad.CreateInstanceAndUnwrap(
                                GetType(Worker).Assembly.FullName, "Worker"),
                            Worker)
    
  2. Appelez une méthode de l’objet Worker qui lève une exception. Dans cet exemple, la méthode Thrower est appelée deux fois. La première fois, l’argument de méthode est true. La méthode intercepte donc sa propre exception. La deuxième fois, l’argument est false, et la méthode Main() intercepte l’exception dans le domaine d’application par défaut.

    // 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. Placez le code dans la méthode Thrower pour contrôler si la méthode gère sa propre exception.

    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
    

Exemple

L’exemple suivant crée un domaine d’application nommé AD1 et ajoute un gestionnaire d’événements à l’événement FirstChanceException du domaine d’application. L’exemple crée une instance de la classe Worker dans le domaine d’application et appelle une méthode nommée Thrower qui lève une ArgumentException. En fonction de la valeur de son argument, la méthode intercepte l’exception ou ne parvient pas à la gérer.

Chaque fois que la méthode Thrower lève une exception dans AD1, l’événement FirstChanceException est déclenché dans AD1, et le gestionnaire d’événements affiche un message. Le runtime recherche ensuite un gestionnaire d’exceptions. Dans le premier cas, le gestionnaire d’exceptions est trouvé dans AD1. Dans le deuxième cas, l’exception n’est pas gérée dans AD1 ; elle est interceptée dans le domaine d’application par défaut.

Notes

Le nom du domaine d’application par défaut est identique au nom de l’exécutable.

Si vous ajoutez un gestionnaire pour l’événement FirstChanceException au domaine d’application par défaut, l’événement est déclenché et géré avant que le domaine d’application par défaut ne gère l’exception. Pour afficher cela, ajoutez le code C# AppDomain.CurrentDomain.FirstChanceException += FirstChanceException; (en Visual Basic, AddHandler AppDomain.CurrentDomain.FirstChanceException, FirstChanceException) au début de 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

Voir aussi