Практическое руководство. Получение уведомлений об исключении First-Chance
Примечание.
Эта статья связана с .NET Framework. Он не применяется к более новым реализациям .NET, включая .NET 6 и более поздние версии.
Событие FirstChanceException класса AppDomain позволяет получать уведомление о том, что исключение было выброшено, до того как общая среда выполнения начала поиск обработчиков исключений.
Событие вызывается на уровне домена приложения. Поток выполнения может проходить через несколько доменов приложений, поэтому исключение, которое необработано в одном домене приложения, может обрабатываться в другом домене приложения. Уведомление происходит в каждом домене приложения, который добавил обработчик для события, пока домен приложения не будет обрабатывать исключение.
В процедурах и примерах этой статьи показано, как получать уведомления об исключениях первого шанса в простой программе с одним доменом приложения и в создаваемом домене приложения.
Более сложный пример, охватывающий несколько доменов приложений, см. в примере для события FirstChanceException.
Получение уведомлений об исключениях First-Chance в домене приложений по умолчанию
В следующей процедуре точка входа для приложения, Main()
метод выполняется в домене приложения по умолчанию.
Демонстрация уведомлений об исключениях первого шанса в домене приложения по умолчанию
Определите обработчик событий для 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 {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
Создайте исключение и перехватите его. Прежде чем среда выполнения найдет обработчик исключений, вызывается событие FirstChanceException и отображается сообщение. За этим сообщением следует сообщение, отображаемое обработчиком исключений.
try { throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName); } catch (ArgumentException ex) { Console.WriteLine($"ArgumentException caught in {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
Бросьте исключение, но не перехватывайте его. Прежде чем среда выполнения ищет обработчик исключений, событие FirstChanceException вызывается и отображается сообщение. Обработчик исключений отсутствует, поэтому приложение завершает работу.
throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName); } }
Throw New ArgumentException("Thrown in " & AppDomain.CurrentDomain.FriendlyName) End Sub End Class
Код, показанный на первых трех шагах этой процедуры, формирует полное консольное приложение. Выходные данные приложения зависят от имени файла .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()
Получение уведомлений First-Chance исключений в другом домене приложения
Если программа содержит несколько доменов приложения, вы можете выбрать, какие домены приложений получают уведомления.
Чтобы получать уведомления об исключениях первого шанса в домене приложения, который вы создаёте
Определите обработчик событий для FirstChanceException события. В этом примере используется
static
метод (Shared
метод в Visual Basic), который выводит имя домена приложения, в котором было обработано событие, и свойство исключения Message .static void FirstChanceHandler(object source, FirstChanceExceptionEventArgs e) { Console.WriteLine($"FirstChanceException event raised in {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
Создайте домен приложения и добавьте обработчик событий в FirstChanceException событие для этого домена приложения. В этом примере домен приложения называется
AD1
.AppDomain ad = AppDomain.CreateDomain("AD1"); ad.FirstChanceException += FirstChanceHandler;
Dim ad As AppDomain = AppDomain.CreateDomain("AD1") AddHandler ad.FirstChanceException, AddressOf FirstChanceHandler
Это событие можно обрабатывать в домене приложения по умолчанию таким же образом. Используйте свойство
static
(Shared
в Visual Basic) AppDomain.CurrentDomain вMain()
, чтобы получить ссылку на домен приложения по умолчанию.
Демонстрация уведомлений о первичном исключении в домене приложения
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)
Вызов метода
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 (ArgumentException ex) { Console.WriteLine($"ArgumentException caught in {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
Поместите код в
Thrower
метод, чтобы контролировать, обрабатывает ли метод собственное исключение.if (catchException) { try { throw new ArgumentException("Thrown in " + AppDomain.CurrentDomain.FriendlyName); } catch (ArgumentException ex) { Console.WriteLine($"ArgumentException caught in {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
, в AD1
поднимается событие FirstChanceException, а обработчик событий отображает сообщение. Затем среда выполнения ищет обработчик исключений. В первом случае обработчик исключений найден в AD1
. Во втором случае исключение остаётся необработанным в AD1
, и вместо этого оно перехватывается в домене приложения по умолчанию.
Примечание.
Имя домена приложения по умолчанию совпадает с именем исполняемого файла.
При добавлении обработчика для события в домене приложения по умолчанию, событие вызывается и обрабатывается перед тем, как домен приложения по умолчанию обрабатывает исключение. Чтобы увидеть это, добавьте код C# AppDomain.CurrentDomain.FirstChanceException += FirstChanceException;
(в Visual Basic AddHandler AppDomain.CurrentDomain.FirstChanceException, FirstChanceException
) в начале 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 {AppDomain.CurrentDomain.FriendlyName}: {ex.Message}");
}
}
static void FirstChanceHandler(object source, FirstChanceExceptionEventArgs e)
{
Console.WriteLine($"FirstChanceException event raised in {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 {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