当另一个域抛出异常时应用程序崩溃

Posted

技术标签:

【中文标题】当另一个域抛出异常时应用程序崩溃【英文标题】:Application crash when anoter domain throws exception 【发布时间】:2013-05-02 21:26:15 【问题描述】:

我正在学习 C#。我阅读了 Andrew Troelsen “C# and the .NET Platform”和 Jeffrey Richter 的“CLR via 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 并在另一个 AppDomain 中运行它们

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();(也许我做错了?)未处理的异常将删除它运行的域,但默认域将起作用。但在我的情况下 - 默认域在另一个域中发生异常后崩溃。请告诉我我做错了什么?

【问题讨论】:

你错了。 任何应用程序域中未处理的异常将导致整个过程中断。 您要么需要处理异常,要么会导致整个过程中断。 ***.com/questions/7071957/… 好的,有什么办法可以捕捉到这个异常,在 MessageBox 中显示“PluginsWithException crashed”之类的东西,应用程序不会崩溃? 如您所见,我尝试使用“domain.FirstChanceException += domain_FirstChanceException;”,但没有帮助( 如果你不想让它崩溃,你需要处理异常。尝试捕获 NotImplementedException 【参考方案1】:

如果您确实需要,有一种方法可以对此进行控制。我们这样做是因为我们的插件可以在我们的团队之外编写,并且我们尽可能地防止我们的应用程序因为其他人的插件而崩溃。

所以我们的应用程序将删除引发异常的 AppDomain,通知用户,然后继续。或者,如果异常来自主 AppDomain,它将简单地 FailFast。

在您的 App.config 中,您需要以下内容:

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

这将恢复为未处理异常的旧行为,并允许您自行决定是终止整个进程还是仅终止 AppDomain。

您仍然需要处理一些其他问题,例如找出哪些异常来自哪个 AppDomain。

另一个问题是并非所有异常都是可序列化的,这意味着当它们跨越 AppDomain 边界时,一些异常会变成 SerializationException。

因为我们的插件实现了一个公共基类,所以我们通过将未处理的异常处理程序放在插件本身中来解决这些问题。然后我们连接到AppDomain.CurrentDomain.UnhandledExceptionTaskScheduler.UnobservedTaskException 并调用AppDomain.Unload(AppDomain.CurrentDomain) 来终止加载项。

这并不完美,但它对我们的项目非常有效。

【讨论】:

【参考方案2】:

来自子 AppDomain 的未处理异常将导致子 AppDomain 崩溃,然后它会在您的主 AppDomain 中抛出。如果你不在那里处理它,主要的AppDomain 也会下降。 FirstChanceException 不处理未处理的异常。检查FirstChanceException 事件的文档。它会针对您的应用程序抛出的所有异常引发,即使是您正在处理的异常。它使您有机会检查所有抛出的异常(已处理或未处理)。

对加载项的所有调用都应该在 try/catch 块中。在那里捕获所有异常并记录它们。您甚至可以将插件标记为不可靠(因为它不稳定),并且在您的应用下次启动时默认不加载它。或者让用户决定做什么。 MS Office 应用程序用于禁用不稳定的插件(导致应用程序崩溃的插件),然后用户必须从 about 对话框再次启用它们(我开发 MS Office 插件已经有一段时间了,我不知道如果他们在 Office 2010 及更高版本中遵循相同的方法)。查看System.AddIn 团队的这个示例,了解如何使用detect add-in failures。它还提到,无论您做什么,来自子 AppDomain 的子线程的未处理异常都会导致整个进程停止。

【讨论】:

以上是关于当另一个域抛出异常时应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

防止应用崩溃

两次抛出的相同异常使 WPF 崩溃

当我不知道它可能会抛出哪里时,如何记录异常?

如何防止后台线程抛出的异常让程序崩溃退出

WCF - 抛出许多许多第一次机会异常 - 然后使应用程序崩溃

swift - 异常处理