方法: 初回例外通知を受け取る

Note

この記事は .NET Framework に固有のものです。 .NET 6 以降のバージョンを含め、.NET の新しい実装には適用されません。

AppDomain クラスの FirstChanceExceptionイベントを使用すると、共通言語ランタイムが例外ハンドラーの検索を開始する前に、例外がスローされたことを知らせる通知を受け取ることができます。

このイベントは、アプリケーション ドメイン レベルで発生します。 実行スレッドは複数のアプリケーション ドメインを通過する可能性があるため、あるアプリケーション ドメインでハンドルされない例外が別のアプリケーション ドメインでハンドルされることもあります。 通知は、イベントのハンドラーを追加した各アプリケーション ドメインで発生し、いずれかのアプリケーション ドメインで例外がハンドルされるまで続行されます。

ここで紹介するプロシージャと例では、1 つのアプリケーション ドメインを実装する単純なプログラムで初回例外通知を受け取る方法と、作成したアプリケーション ドメインで初回例外通知を受け取る方法について説明します。

複数のアプリケーション ドメインにわたる複雑な例については、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
    

    このプロシージャの最初の 3 つのステップに示されているコードは、完全なコンソール アプリケーションを形成します。 既定のアプリケーション ドメインの名前は .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 プロパティを出力します。

    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 (Visual Basic では Shared) 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 メソッドが 2 回呼び出されます。 1 回目のメソッド引数は true であるため、メソッドは自身の例外をキャッチします。 2 回目の引数は 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 で例外をスローするたびに、AD1FirstChanceException イベントが発生し、イベント ハンドラーによりメッセージが表示されます。 次に、ランタイムによって例外ハンドラーが検索されます。 最初のケースでは、例外ハンドラーは AD1 で見つかります。 2 番目のケースでは、例外は AD1 でハンドルされず、代わりに既定のアプリケーション ドメインでキャッチされます。

Note

既定のアプリケーション ドメインの名前は、実行可能ファイルの名前と同じです。

既定のアプリケーション ドメインに FirstChanceException イベントのハンドラーを追加すると、既定のアプリケーション ドメインが例外をハンドルする前に、イベントが発生して処理されます。 これを確認するには、Main() の先頭に C# コード AppDomain.CurrentDomain.FirstChanceException += FirstChanceException; (Visual Basic では AddHandler AppDomain.CurrentDomain.FirstChanceException, FirstChanceException) を追加します。

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

関連項目