Share via


HOW TO:接收第一個可能發生的例外狀況通知

AppDomain 類別的 FirstChanceException 事件可讓您在 Common Language Runtime 開始搜尋例外處理常式之前,接收擲回例外狀況的通知。

這個事件會在應用程式定義域層級中引發。 一個例外狀況的執行緒可以通過多個應用程式定義域,所以在一個應用程式定義域中未處理的例外狀況可以在另一個應用程式定義域中處理。 在加入事件處理常式的每個應用程式定義域中,都會發生通知,直到應用程式定義域處理例外狀況為止。

本文中的程序和範例顯示如何在具有一個應用程式定義域的簡單程式,以及在您所建立應用程式定義域中接收第一個可能發生的例外狀況通知。

如需深入了解較複雜之跨多個應用程式定義域的範例,請參閱 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