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

【问题讨论】:

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

标签: c# exception .net-assembly appdomain


【解决方案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 的子线程的未处理异常都会导致整个进程停止。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多