Мы используем System.AddIn для загрузки надстроек в отдельные дочерние домены приложений, и мы выгружаем надстройку AppDomain, если у нее есть необработанное исключение.
Поскольку стандартное поведение .NET начиная с версии 2.0 заключалось в том, чтобы завершить весь процесс, если у какого-либо домена приложений есть необработанное исключение, мы делаем это с помощью параметра «legacyUnhandledExceptionPolicy» в нашем файле App.config, а затем вручную срываем процесс если необработанное исключение было в основном домене приложений, или выгрузка соответствующего домена приложений, если оно было в надстройке.
Все это прекрасно работает, за исключением одной небольшой проблемы: необработанные исключения всегда всплывают от дочерних доменов приложений к основному, и если они не сериализуемы, они не могут успешно пересечь границу домена приложений.
Вместо этого мы получаем исключение SerializationException, которое отображается как исключение UnhandledException в главном домене приложений, в результате чего наше приложение разрывается.
Я могу придумать несколько возможных решений этой проблемы:
Мы не можем разорвать процесс для необработанных исключений SerializationException (yuck).
Мы могли бы остановить распространение исключений из дочернего домена приложений на основной домен приложений.
Мы могли бы заменить несериализуемые исключения сериализуемыми, возможно, используя суррогаты сериализации и связыватели сериализации. [Изменить: см. Конец, почему это невозможно с помощью суррогатов]
Однако первый довольно ужасен, и мне не удалось выяснить, как сделать любой из других вариантов с удаленным удалением через AppDomain.
Может кто-нибудь дать совет? Любая помощь приветствуется.
Для воспроизведения создайте консольное приложение со следующим App.config:
<?xml version="1.0"?>
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="true"/>
</runtime>
</configuration>
и следующий код:
class Program
{
private class NonSerializableException : Exception
{
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += MainDomain_UnhandledException;
AppDomain childAppDomain = AppDomain.CreateDomain("Child");
childAppDomain.UnhandledException += ChildAppDomain_UnhandledException;
childAppDomain.DoCallBack(
() => new Thread(delegate() { throw new NonSerializableException(); }).Start());
Console.ReadLine();
}
static void MainDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("Main AppDomain Unhandled Exception: " + e.ExceptionObject);
Console.WriteLine();
}
static void ChildAppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("Child AppDomain Unhandled Exception: " + e.ExceptionObject);
Console.WriteLine();
}
}
РЕДАКТИРОВАТЬ: я использовал рефлектор, чтобы увидеть, есть ли какой-нибудь способ получить доступ к BinaryFormatter, используемому в удаленном удаленном взаимодействии между доменами приложений, но в конечном итоге это код внутри класса CrossAppDomainSerializer:
internal static void SerializeObject(object obj, MemoryStream stm)
{
BinaryFormatter formatter = new BinaryFormatter();
RemotingSurrogateSelector selector = new RemotingSurrogateSelector();
formatter.SurrogateSelector = selector;
formatter.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
formatter.Serialize(stm, obj, null, false);
}
Таким образом, он создает средство форматирования локально в методе, и нет никакого способа присоединить мой собственный суррогат ... Я думаю, что это делает любые дополнительные усилия в этом направлении бесполезными.