2013-05-02 1 views
0

Я изучаю C#. Я читал книги Эндрю Троелсена «C# и .NET Platform» и «CLR через C#» Джеффри Рихтера. Теперь я пытаюсь создать приложение, которое будет загружать сборки из какого-либо каталога, подталкивать их к AppDomain и запускать включенный метод (приложение, которое поддерживает подключаемые модули). Вот DLL, где общий интерфейс. Я добавляю его в свое приложение и во все DLL-файлы с плагинами. MainLib.DLLСбой приложения, когда исключение домена anoter исключает

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 и запускает их в другой AppDomain's

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(); в другом домене (возможно, я делаю это неправильно?) Необработанное исключение приведет к удалению домена, где он выполняется, но домен по умолчанию будет работать. Но в моем случае - по умолчанию происходит сбой домена после исключения в другом домене. Пожалуйста, скажи мне, что я делаю неправильно?

+1

Вы ошибаетесь. Необработанное исключение в * любом appdomain * приведет к уничтожению * всего процесса *. – dlev

+0

Вам либо нужно обработать исключение, либо это приведет к разрушению всего процесса. http://stackoverflow.com/questions/7071957/appdomain-handling-the-exceptions – Dave

+0

Хорошо, есть ли способы поймать это исключение, показать MessageBox с чем-то вроде «Сбой PluginsWithException», и приложение не будет разбиваться? –

ответ

3

Существует способ получить контроль над этим, если вам действительно нужно. Мы делаем это, потому что наши надстройки могут быть написаны за пределами нашей команды, и, насколько это возможно, мы пытаемся предотвратить сбой приложения из-за чужих надстроек.

Таким образом, наше приложение уничтожит AppDomain, который исключил исключение, сообщит пользователю и продолжит работу. Или это просто FailFast, если исключение произошло из основного AppDomain.

В вашем App.config вам необходимо следующее:

<configuration> 
<runtime> 
    <legacyUnhandledExceptionPolicy enabled="true" /> 
</runtime> 
</configuration> 

Это возвращается к унаследованному поведению, связанным с необработанными исключениями и позволяет решить для себя, стоит ли убить весь процесс или только AppDomain.

У вас все еще есть другие проблемы, связанные с разработкой каких исключений, из которых AppDomain.

Другая проблема заключается в том, что не все исключения являются сериализуемыми, что означает, что некоторые из них превратятся в исключение SerializationException, когда пересекают границу AppDomain.

Поскольку наши надстройки реализуют общий базовый класс, мы обошли эти проблемы, поставив необработанные обработчики исключений в самих надстроек. Затем мы подключаем к AppDomain.CurrentDomain.UnhandledException и TaskScheduler.UnobservedTaskException и вызываем AppDomain.Unload(AppDomain.CurrentDomain) для завершения надстройки.

Это не идеально, но он очень хорошо работает для нашего проекта.

1

Необработанное исключение из ребенка AppDomain приведет к сбиванию ребенка AppDomain, а затем оно будет выброшено в ваш основной AppDomain. Если вы не справитесь с этим, основной AppDomain также снизится. FirstChanceException не обрабатывает необработанные исключения. Проверьте документацию на мероприятии FirstChanceException. Он создан для всех исключений, создаваемых вашим приложением, даже для тех, которые вы обрабатываете. Это дает вам возможность изучить все брошенные (обработанные или необработанные) исключения.

Все вызовы надстроек должны быть в блоках try/catch. Поймайте там все исключения и запишите их. Вы даже можете отметить плагин как ненадежный (потому что он нестабилен) и не загружать его по умолчанию при следующем запуске приложения. Или позвольте пользователю решить, что делать. Приложение MS Office, используемое для отключения нестабильных подключаемых модулей (те, которые разбивали приложение), а затем пользователь должен был включить их снова из диалогового окна (это было время с тех пор, как я разработал надстройки MS Office, и я не знаю если они будут придерживаться того же подхода в Office 2010 и более поздних версиях). Взгляните на этот пример командой System.AddIn о том, как detect add-in failures. В нем также упоминается, что необработанные исключения из дочерних потоков ребенка AppDomain приведут весь процесс вниз независимо от того, что вы делаете.