Сбой приложения при выдаче исключения другим доменом
Я изучаю C#. Я читал книги Эндрю Троелсена "C# и платформа.NET" и Джеффри Рихтера "CLR через C#". Теперь я пытаюсь создать приложение, которое будет загружать сборки из некоторого каталога, помещать их в AppDomain и запускать включенный метод (приложение, которое поддерживает плагины). Вот DLL, где находится общий интерфейс. Я добавляю его в свое приложение и во все библиотеки DLL с плагинами. MainLib.DLL
namespace MainLib
{
public interface ICommonInterface
{
void ShowDllName();
}
}
Вот плагины: PluginWithOutException
namespace PluginWithOutException
{
public class WithOutException : MarshalByRefObject, ICommonInterface
{
public void ShowDllName()
{
MessageBox.Show("PluginWithOutException");
}
public WithOutException()
{
}
}
}
и еще один: PluginWithException
namespace PluginWithException
{
public class WithException : MarshalByRefObject, ICommonInterface
{
public void ShowDllName()
{
MessageBox.Show("WithException");
throw new NotImplementedException();
}
}
}
А вот приложение, которое загружает библиотеки DLL и запускает их в другом домене приложений.
namespace Plug_inApp
{
class Program
{
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(CreateDomainAndLoadAssebly, @"E:\Plugins\PluginWithException.dll");
Console.ReadKey();
}
public static void CreateDomainAndLoadAssebly(object name)
{
string assemblyName = (string)name;
Assembly assemblyToLoad = null;
AppDomain domain = AppDomain.CreateDomain(string.Format("{0} Domain", assemblyName));
domain.FirstChanceException += domain_FirstChanceException;
try
{
assemblyToLoad = Assembly.LoadFrom(assemblyName);
}
catch (FileNotFoundException)
{
MessageBox.Show("Can't find assembly!");
throw;
}
var theClassTypes = from t in assemblyToLoad.GetTypes()
where t.IsClass &&
(t.GetInterface("ICommonInterface") != null)
select t;
foreach (Type type in theClassTypes)
{
ICommonInterface instance = (ICommonInterface)domain.CreateInstanceFromAndUnwrap(assemblyName, type.FullName);
instance.ShowDllName();
}
}
static void domain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message);
}
}
}
Я ожидаю, что если я бегу instance.ShowDllName();
в другом домене (возможно, я делаю это неправильно?) необработанное исключение удалит домен, в котором оно выполняется, но домен по умолчанию будет работать. Но в моем случае - домен по умолчанию падает после исключения в другом домене. Пожалуйста, скажите мне, что я делаю не так?
2 ответа
Есть способ получить некоторый контроль над этим, если вам действительно нужно. Мы делаем это потому, что наши надстройки могут быть написаны вне нашей команды, и мы стараемся, насколько это возможно, предотвратить сбой нашего приложения из-за надстроек других людей.
Таким образом, наше приложение отключит домен приложения, который вызвал исключение, проинформирует пользователя и продолжит работу. Или просто FailFast, если исключение пришло из основного домена приложения.
В вашем App.config вам нужно следующее:
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="true" />
</runtime>
</configuration>
Это вернется к унаследованному поведению для необработанных исключений и позволит вам самим решить, уничтожить ли весь процесс или только домен приложения.
У вас по-прежнему будут возникать другие проблемы, такие как выяснение того, какие исключения возникли из какого AppDomain.
Другая проблема состоит в том, что не все исключения являются сериализуемыми, что означает, что некоторые из них превратятся в SerializationException, когда они пересекают границу AppDomain.
Поскольку наши надстройки реализуют общий базовый класс, мы решили эти проблемы, поместив необработанные обработчики исключений в сами надстройки. Затем мы подключаемся AppDomain.CurrentDomain.UnhandledException
а также TaskScheduler.UnobservedTaskException
и позвонить AppDomain.Unload(AppDomain.CurrentDomain)
прекратить надстройку.
Это не идеально, но очень хорошо работает для нашего проекта.
Необработанное исключение из ребенка AppDomain
сбьет ребенка AppDomain
и тогда он будет брошен в ваш главный AppDomain
, Если вы не справитесь с этим, основной AppDomain
также будет идти вниз. FirstChanceException
не обрабатывает необработанные исключения. Проверьте документацию по событию FirstChanceException. Он вызывается для всех исключений, выдвигаемых вашим приложением, даже для тех, которые вы обрабатываете. Это дает вам возможность изучить все исключения (обработанные или необработанные).
Все вызовы надстроек должны быть в блоках try/catch. Поймай все исключения и зарегистрируй их. Вы даже можете пометить плагин как ненадежный (потому что он нестабилен) и не загружать его по умолчанию при следующем запуске приложения. Или пусть пользователь решит, что делать. Приложение MS Office раньше отключало нестабильные плагины (те, которые вызывали сбой приложения), а затем пользователю приходилось снова включать их из диалогового окна о программе (это было давно, так как я разрабатывал надстройки MS Office, и я не знаю, если они следуют тому же подходу в Office 2010 и более поздних версиях). Посмотрите на этот пример System.AddIn
команда по обнаружению сбоев надстроек. Также упоминается, что необработанные исключения из дочерних потоков дочерних AppDomain
принесет вниз весь процесс, независимо от того, что вы делаете.