Share via


如何:接收首次异常通知

AppDomain 类的 FirstChanceException 事件可使您在公共语言运行时开始搜索异常处理程序之前,能够收到已引发异常的通知。

该事件在应用程序域级别引发。 由于执行线程可以通过多个应用程序域,因此可以在一个应用程序域中处理另一个应用程序域中未处理的异常。 在已添加该事件的处理程序的每个应用程序域中将会出现通知,直到某个应用程序域处理该异常。

本文中的过程和示例说明如何在具有一个应用程序域的简单程序中和您创建的应用程序域中接收首次异常通知。

有关跨多个应用程序域的更复杂的示例,请参见 FirstChanceException 事件的示例。

在默认应用程序域中接收首次异常通知

在下面的过程中,应用程序的入口点(Main() 方法)在默认应用程序域中运行。

在默认应用程序域中演示首次异常通知

  1. 使用 lambda 函数为 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 方法(在 Visual Basic 中为 Shared 方法),该方法可输出在其中处理事件的应用程序域的名称和异常的 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;
    

    可以按照相同的方式在默认应用程序域中处理此事件。 使用 Main() 中的 static(在 Visual Basic 中为 Shared)AppDomain.CurrentDomain 属性可获取对默认应用程序域的引用。

在应用程序域中演示首次异常通知

  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 中引发异常时,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