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

Dim*_*nyi 0 c# exception appdomain .net-assembly

我正在学习C#。我阅读了 Andrew Troelsen 的《C# and the .NET Platform》和 Jeffrey Richter 的《CLR via C#》等书。现在,我正在尝试创建应用程序,它将从某个目录加载程序集,将它们推送到 AppDomain 并运行包含的方法(支持插件的应用程序)。这是 DLL,其中是公共接口。我将其添加到我的应用程序以及所有带插件的 DLL 中。主库.DLL

namespace MainLib
{
public interface ICommonInterface
{
    void ShowDllName();
}
}
Run Code Online (Sandbox Code Playgroud)

这是插件:PluginWithOutException

namespace PluginWithOutException
{
public class WithOutException : MarshalByRefObject, ICommonInterface
{
    public void ShowDllName()
    {
        MessageBox.Show("PluginWithOutException");
    }

    public WithOutException()
    {

    }
}
}
Run Code Online (Sandbox Code Playgroud)

另一个:PluginWithException

namespace PluginWithException
{
public class WithException : MarshalByRefObject, ICommonInterface
{
    public void ShowDllName()
    {
        MessageBox.Show("WithException");
        throw new NotImplementedException();
    }
}
}
Run Code Online (Sandbox Code Playgroud)

这是一个应用程序,它加载 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);
    }
}
}
Run Code Online (Sandbox Code Playgroud)

我希望,如果我instance.ShowDllName();在另一个域中运行(也许我做错了?),未处理的异常将删除它运行的域,但默认域将起作用。但就我而言 - 在另一个域中发生异常后,默认域崩溃。请告诉我我做错了什么?

Jam*_*ley 5

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

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

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

<configuration>
 <runtime>
  <legacyUnhandledExceptionPolicy enabled="true" />
 </runtime>
</configuration>
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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