Практическое руководство. Получение уведомлений о первом этапе обработки исключений
Событие FirstChanceException класса AppDomain позволяет получать уведомления о порождении исключений до того, как среда CLR начнет искать обработчики исключений.
Событие порождается на уровне домена приложения. Поток выполнения может проходить несколько доменов приложения, поэтому необработанное в одном домене исключения может быть обработано в другом домене. Уведомление происходит во всех доменах приложения, добавивших обработчик для этого события, пока исключение не будет обработано.
Процедуры и примеры в этой статье демонстрируют, как можно получать уведомления о первичных исключениях в рамках простой программы с одним доменом приложения, а также в созданном пользователем домене приложения.
Более развернутый пример с несколькими доменами приложения см. в описании события FirstChanceException.
Получение уведомлений о первичном исключении в домене приложения по умолчанию
В представленной ниже процедуре точка входа для приложения, метод Main(), запускается в домене приложения по умолчанию.
Демонстрация уведомлений о первичном исключении в домене приложения по умолчанию
Определите обработчик событий для события 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); };
Породите исключение и перехватите его. Еще до того, как среда выполнения найдет обработчик исключений, будет вызвано событие 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); }
Вызовите исключение, но не перехватывайте его. Еще до того, как среда выполнения начнет искать обработчик исключений, будет вызвано событие 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() */
Получение уведомлений о первичном исключении в другом домене приложения
Если в программе имеется несколько доменов приложения, можно выбирать, какие из них должны получать уведомления.
Получение уведомлений о первичном исключении в созданном пользователем домене приложения
Определите обработчик событий FirstChanceException. В примере используется метод static (Shared в Visual Basic), выводящий имя домена приложения, обработавшего исключение, и свойство 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); }
Создайте домен приложения и добавьте обработчик событий FirstChanceException для этого домена приложения. В этом примере домен приложения называется AD1.
Dim ad As AppDomain = AppDomain.CreateDomain("AD1") AddHandler ad.FirstChanceException, AddressOf FirstChanceHandler
AppDomain ad = AppDomain.CreateDomain("AD1"); ad.FirstChanceException += FirstChanceHandler;
Данное событие можно аналогичным образом обработать в домене приложения по умолчанию. Используйте свойство AppDomain.CurrentDomain с модификатором static (Shared в Visual Basic) в методе Main(), чтобы получить ссылку на домен приложения по умолчанию.
Демонстрация уведомлений о первичном исключении в домене приложения
Создайте объект Worker в домене приложения, созданном в предыдущей процедуре. Класс Worker должен быть открытым наследником MarshalByRefObject, как показано в полном примере в конце статьи.
Dim w As Worker = CType(ad.CreateInstanceAndUnwrap( Assembly.GetExecutingAssembly().FullName, "Worker"), Worker)
Worker w = (Worker) ad.CreateInstanceAndUnwrap( Assembly.GetExecutingAssembly().FullName, "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 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); }
Поместите в метод 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 в нем порождается событие 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(), чтобы предотвратить закрытие командного окна до того, как вы успеете прочитать выходные данные.