AppDomain.FirstChanceException イベント

定義

アプリケーション ドメイン内の例外ハンドラーに対する呼び出し履歴をランタイムが検索する前に、マネージド コード内で例外がスローされた場合に発生します。

public:
 event EventHandler<System::Runtime::ExceptionServices::FirstChanceExceptionEventArgs ^> ^ FirstChanceException;
public event EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>? FirstChanceException;
public event EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs> FirstChanceException;
[add: System.Security.SecurityCritical]
[remove: System.Security.SecurityCritical]
public event EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs> FirstChanceException;
member this.FirstChanceException : EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs> 
[<add: System.Security.SecurityCritical>]
[<remove: System.Security.SecurityCritical>]
member this.FirstChanceException : EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs> 
Public Custom Event FirstChanceException As EventHandler(Of FirstChanceExceptionEventArgs) 

イベントの種類

属性

次の例では、 を使用して AD3という名前AD0の一連のアプリケーション ドメインを作成し、各アプリケーション ドメインに オブジェクトをWorker作成します。 各 Worker オブジェクトは、最後のアプリケーション ドメイン内の を Worker 除き Worker 、次のアプリケーション ドメイン内の オブジェクトへの参照を持ちます。 イベントは FirstChanceException 、 を除く AD1すべてのアプリケーション ドメインで処理されます。

注意

複数のアプリケーション ドメインでの初回例外通知を示すこの例に加えて、「 方法: First-Chance例外通知を受信する」で簡単なユース ケースを見つけることができます。

アプリケーション ドメインが作成されると、既定のアプリケーション ドメインは、最初のアプリケーション ドメインの TestException メソッドを呼び出します。 各Workerオブジェクトは、最後WorkerTestException処理またはハンドルされない例外をスローするまで、次のアプリケーション ドメインの メソッドを呼び出します。 したがって、現在のスレッドはすべてのアプリケーション ドメインを通過し TestException 、各アプリケーション ドメインのスタックに追加されます。

最後 Worker のオブジェクトが例外を処理すると、 FirstChanceException イベントは最後のアプリケーション ドメインでのみ発生します。 他のアプリケーション ドメインでは例外を処理する機会がないため、イベントは発生しません。

最後 Worker のオブジェクトが例外を処理しない場合、 FirstChanceException イベント ハンドラーを持つ各アプリケーション ドメインでイベントが発生します。 各イベント ハンドラーが完了すると、既定のアプリケーション ドメインによって例外がキャッチされるまで、スタックはアンワインドを続行します。

注意

イベントが既定のアプリケーション ドメインに近づくにつれてスタックの表示がどのように拡大するかを確認するには、イベント ハンドラーで を FirstChanceHandlere.Exception変更e.Exception.Messageします。 がアプリケーション ドメインの境界を越えて呼び出されると TestException 、プロキシに対して 1 回、スタブに対して 1 回の 2 回表示されます。

using System;
using System.Reflection;
using System.Runtime.ExceptionServices;

class FirstChanceExceptionSnippet
{
    static void Main()
    {
        AppDomain.CurrentDomain.FirstChanceException += FirstChanceHandler;

        // Create a set of application domains, with a Worker object in each one.
        // Each Worker object creates the next application domain.
        AppDomain ad = AppDomain.CreateDomain("AD0");
        Worker w = (Worker) ad.CreateInstanceAndUnwrap(
                                typeof(Worker).Assembly.FullName, "Worker");
        w.Initialize(0, 3);

        Console.WriteLine("\r\nThe last application domain throws an exception and catches it:");
        Console.WriteLine();
        w.TestException(true);

        try
        {
            Console.WriteLine(
                "\r\nThe last application domain throws an exception and does not catch it:");
            Console.WriteLine();
            w.TestException(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
{
    private AppDomain ad = null;
    private Worker w = null;

    public void Initialize(int count, int max)
    {
        // Handle the FirstChanceException event in all application domains except
        // AD1.
        if (count != 1)
        {
            AppDomain.CurrentDomain.FirstChanceException += FirstChanceHandler;
        }

        // Create another application domain, until the maximum is reached.
        // Field w remains null in the last application domain, as a signal
        // to TestException().
        if (count < max)
        {
            int next = count + 1;
            ad = AppDomain.CreateDomain("AD" + next);
            w = (Worker) ad.CreateInstanceAndUnwrap(
                             typeof(Worker).Assembly.FullName, "Worker");
            w.Initialize(next, max);
        }
    }

    public void TestException(bool handled)
    {
        // As long as there is another application domain, call TestException() on
        // its Worker object. When the last application domain is reached, throw a
        // handled or unhandled exception.
        if (w != null)
        {
            w.TestException(handled);
        }
        else if (handled)
        {
            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);
        }
    }

    static void FirstChanceHandler(object source, FirstChanceExceptionEventArgs e)
    {
        Console.WriteLine("FirstChanceException event raised in {0}: {1}",
            AppDomain.CurrentDomain.FriendlyName, e.Exception.Message);
    }
}

/* This example produces output similar to the following:

The last application domain throws an exception and catches it:

FirstChanceException event raised in AD3: Thrown in AD3
ArgumentException caught in AD3: Thrown in AD3

The last application domain throws an exception and does not catch it:

FirstChanceException event raised in AD3: Thrown in AD3
FirstChanceException event raised in AD2: Thrown in AD3
FirstChanceException event raised in AD0: Thrown in AD3
FirstChanceException event raised in Example.exe: Thrown in AD3
ArgumentException caught in Example.exe: Thrown in AD3
 */
open System
open System.Runtime.ExceptionServices

let firstChanceHandler _ (e: FirstChanceExceptionEventArgs) =
    printfn $"FirstChanceException event raised in {AppDomain.CurrentDomain.FriendlyName}: {e.Exception.Message}"

type Worker() =
    inherit MarshalByRefObject()

    let mutable w = Unchecked.defaultof<Worker>

    member _.Initialize(count, max) =
        // Handle the FirstChanceException event in all application domains except
        // AD1.
        if count <> 1 then
            AppDomain.CurrentDomain.FirstChanceException.AddHandler firstChanceHandler

        // Create another application domain, until the maximum is reached.
        // Field w remains null in the last application domain, as a signal
        // to TestException().
        if count < max then
            let next = count + 1
            let ad = AppDomain.CreateDomain("AD" + string next)
            w <-
                ad.CreateInstanceAndUnwrap(typeof<Worker>.Assembly.FullName, "Worker") :?> Worker
            w.Initialize(next, max)

    member _.TestException(handled) =
        // As long as there is another application domain, call TestException() on
        // its Worker object. When the last application domain is reached, throw a
        // handled or unhandled exception.
        if isNull (box w) then
            w.TestException handled
        elif handled then
            try
                raise (ArgumentException $"Thrown in {AppDomain.CurrentDomain.FriendlyName}")
            with :? ArgumentException as ex ->
                printfn $"ArgumentException caught in {AppDomain.CurrentDomain.FriendlyName}: {ex.Message}"
        else
            raise (ArgumentException $"Thrown in {AppDomain.CurrentDomain.FriendlyName}")

AppDomain.CurrentDomain.FirstChanceException.AddHandler firstChanceHandler

// Create a set of application domains, with a Worker object in each one.
// Each Worker object creates the next application domain.
let ad = AppDomain.CreateDomain "AD0"
let w = ad.CreateInstanceAndUnwrap(typeof<Worker>.Assembly.FullName, "Worker") :?> Worker
w.Initialize(0, 3)

printfn "\nThe last application domain throws an exception and catches it:\n"
w.TestException true

try
    printfn "\nThe last application domain throws an exception and does not catch it:\n"
    w.TestException false
with :? ArgumentException as ex ->
    printfn"ArgumentException caught in {AppDomain.CurrentDomain.FriendlyName}: {ex.Message}"

(* This example produces output similar to the following:

The last application domain throws an exception and catches it:

FirstChanceException event raised in AD3: Thrown in AD3
ArgumentException caught in AD3: Thrown in AD3

The last application domain throws an exception and does not catch it:

FirstChanceException event raised in AD3: Thrown in AD3
FirstChanceException event raised in AD2: Thrown in AD3
FirstChanceException event raised in AD0: Thrown in AD3
FirstChanceException event raised in Example.exe: Thrown in AD3
ArgumentException caught in Example.exe: Thrown in AD3
 *)
Imports System.Reflection
Imports System.Runtime.ExceptionServices

Class Example

    Shared Sub Main()
    
        AddHandler AppDomain.CurrentDomain.FirstChanceException, AddressOf FirstChanceHandler

        ' Create a set of application domains, with a Worker object in each one.
        ' Each Worker object creates the next application domain.
        Dim ad As AppDomain = AppDomain.CreateDomain("AD0")
        Dim w As Worker = CType(ad.CreateInstanceAndUnwrap(
                                GetType(Worker).Assembly.FullName, "Worker"),
                                Worker)
        w.Initialize(0, 3)

        Console.WriteLine(vbCrLf & "The last application domain throws an exception and catches it:")
        Console.WriteLine()
        w.TestException(true)

        Try
            Console.WriteLine(vbCrLf & 
                "The last application domain throws an exception and does not catch it:")
            Console.WriteLine()
            w.TestException(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

    Private ad As AppDomain = Nothing
    Private w As Worker = Nothing

    Public Sub Initialize(ByVal count As Integer, ByVal max As Integer)
    
        ' Handle the FirstChanceException event in all application domains except
        ' AD1.
        If count <> 1
        
            AddHandler AppDomain.CurrentDomain.FirstChanceException, AddressOf FirstChanceHandler

        End If

        ' Create another application domain, until the maximum is reached.
        ' Field w remains Nothing in the last application domain, as a signal 
        ' to TestException(). 
        If count < max
            Dim nextAD As Integer = count + 1
            ad = AppDomain.CreateDomain("AD" & nextAD)
            w = CType(ad.CreateInstanceAndUnwrap(
                      GetType(Worker).Assembly.FullName, "Worker"),
                      Worker)
            w.Initialize(nextAD, max)
        End If
    End Sub

    Public Sub TestException(ByVal handled As Boolean)
    
        ' As long as there is another application domain, call TestException() on
        ' its Worker object. When the last application domain is reached, throw a
        ' handled or unhandled exception.
        If w IsNot Nothing
        
            w.TestException(handled)

        Else If handled
        
            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

    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

' This example produces output similar to the following:
'
'The last application domain throws an exception and catches it:
'
'FirstChanceException event raised in AD3: Thrown in AD3
'ArgumentException caught in AD3: Thrown in AD3
'
'The last application domain throws an exception and does not catch it:
'
'FirstChanceException event raised in AD3: Thrown in AD3
'FirstChanceException event raised in AD2: Thrown in AD3
'FirstChanceException event raised in AD0: Thrown in AD3
'FirstChanceException event raised in Example.exe: Thrown in AD3
'ArgumentException caught in Example.exe: Thrown in AD3

注釈

このイベントは通知のみです。 このイベントを処理しても、例外は処理されず、後続の例外処理にも影響しません。 イベントが発生し、イベント ハンドラーが呼び出されると、共通言語ランタイム (CLR) は例外のハンドラーの検索を開始します。 FirstChanceException は、マネージド例外を最初に調べる機会をアプリケーション ドメインに提供します。

イベントは、アプリケーション ドメインごとに処理できます。 呼び出しの実行中にスレッドが複数のアプリケーション ドメインを通過する場合、CLR がそのアプリケーション ドメインで一致する例外ハンドラーの検索を開始する前に、イベント ハンドラーを登録した各アプリケーション ドメインでイベントが発生します。 イベントが処理されると、そのアプリケーション ドメイン内の一致する例外ハンドラーが検索されます。 何も見つからない場合は、次のアプリケーション ドメインでイベントが発生します。

イベントのイベント ハンドラーで発生するすべての例外を処理する FirstChanceException 必要があります。 それ以外の場合は、 FirstChanceException が再帰的に発生します。 これにより、アプリケーションのスタック オーバーフローと終了が発生する可能性があります。 例外通知の処理中に、メモリ不足やスタック オーバーフローなどのインフラストラクチャ関連の例外が仮想マシンに影響しないように、このイベントのイベント ハンドラーを制約付き実行領域 (CER) として実装することをお勧めします。

このイベントは、イベント ハンドラーがセキュリティクリティカルで 属性を持 HandleProcessCorruptedStateExceptionsAttribute たない限り、アクセス違反などのプロセス状態の破損を示す例外では発生しません。

共通言語ランタイムは、この通知イベントの処理中にスレッドの中止を中断します。

適用対象

こちらもご覧ください