Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 处理子AppDomain中不可序列化的未处理异常_C#_.net_Exception_Serialization_Appdomain - Fatal编程技术网

C# 处理子AppDomain中不可序列化的未处理异常

C# 处理子AppDomain中不可序列化的未处理异常,c#,.net,exception,serialization,appdomain,C#,.net,Exception,Serialization,Appdomain,我们使用System.AddIn将外接程序加载到单独的子AppDomain中,如果外接程序AppDomain中存在未处理的异常,则卸载该外接程序 由于.NET 2.0版的默认行为是,如果任何AppDomain存在未处理的异常,则会中断整个进程,因此我们可以使用App.config中的“legacyUnhandledExceptionPolicy”选项,然后在未处理的异常位于主AppDomain中时手动中断进程,或者卸载适当的AppDomain(如果它位于外接程序中) 这一切都很好,除了一个小问题

我们使用System.AddIn将外接程序加载到单独的子AppDomain中,如果外接程序AppDomain中存在未处理的异常,则卸载该外接程序

由于.NET 2.0版的默认行为是,如果任何AppDomain存在未处理的异常,则会中断整个进程,因此我们可以使用App.config中的“legacyUnhandledExceptionPolicy”选项,然后在未处理的异常位于主AppDomain中时手动中断进程,或者卸载适当的AppDomain(如果它位于外接程序中)

这一切都很好,除了一个小问题:未处理的异常总是从子AppDomain冒泡到主AppDomain,如果它们不可序列化,则无法成功跨越AppDomain边界

相反,我们得到一个SerializationException,它在主AppDomain中显示为未处理的异常,导致应用程序自行崩溃

我可以想出一些解决这个问题的可能办法:

  • 我们无法为未处理的序列化异常中断进程(恶心)

  • 我们可以阻止异常从子AppDomain传播到主AppDomain

  • 我们可以用可序列化的异常替换不可序列化的异常,也许可以使用序列化代理和序列化绑定。[编辑:有关使用代理无法实现此操作的原因,请参见结尾]

然而,第一个是相当可怕的,我一直没有弄清楚如何做跨AppDomain远程处理的其他任何一个选项

有人能提供一些建议吗?感谢您的帮助

要使用以下App.config创建控制台应用程序:

<?xml version="1.0"?>
<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="true"/>
  </runtime>
</configuration>


编辑:我使用reflector查看是否有任何方法可以访问跨AppDomain远程处理中使用的BinaryFormatter,但它最终在CrossAppDomainSerializer类中的以下代码中结束:

internal static void SerializeObject(object obj, MemoryStream stm)
{
    BinaryFormatter formatter = new BinaryFormatter();
    RemotingSurrogateSelector selector = new RemotingSurrogateSelector();
    formatter.SurrogateSelector = selector;
    formatter.Context = new StreamingContext(StreamingContextStates.CrossAppDomain);
    formatter.Serialize(stm, obj, null, false);
}

所以它在方法中本地创建格式化程序,显然没有办法附加我自己的代理。。。我认为这使得在这方面的任何努力都是徒劳的。

我将处理远程应用程序域中的异常。首先,我将使用异常处理代码创建一个新程序集,然后将其加载到子应用程序域中

下面的代码应该给出一些想法

class Program {
    private class NonSerializableException : Exception { }

    static void Main(string[] args) {
        var childAppDomain = AppDomain.CreateDomain("Child");
        Console.WriteLine("Created child AppDomain #{0}.", childAppDomain.Id);

        // I did not create a new assembly for the helper class because I am lazy :)
        var helperAssemblyLocation = typeof(AppDomainHelper).Assembly.Location;
        var helper = (AppDomainHelper)childAppDomain.CreateInstanceFromAndUnwrap(
                helperAssemblyLocation, typeof(AppDomainHelper).FullName);
        helper.Initialize(UnloadHelper.Instance);

        childAppDomain.DoCallBack(
            () => new Thread(delegate() { throw new NonSerializableException(); }).Start());

        Console.ReadLine();
    }

    private sealed class UnloadHelper : MarshalByRefObject, IAppDomainUnloader {
        public static readonly UnloadHelper Instance = new UnloadHelper();

        private UnloadHelper() { }

        public override object InitializeLifetimeService() {
            return null;
        }

        public void RequestUnload(int id) {
            // Add application domain identified by id into unload queue.
            Console.WriteLine("AppDomain #{0} requests unload.", id);
        }
    }
}

// These two types could be in another helper assembly

public interface IAppDomainUnloader {
    void RequestUnload(int id);
}

public sealed class AppDomainHelper : MarshalByRefObject {
    public void Initialize(IAppDomainUnloader unloader) {
        AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
                unloader.RequestUnload(AppDomain.CurrentDomain.Id);
    }
}

感谢mgronber,但即使使用此解决方案,NonSerializableException仍会作为未经处理的序列化异常传播到父AppDomain。我无法将SerializationException标识为来自子AppDomain的提交,这迫使我要么忽略它,要么将其视为任何其他未处理的异常并终止应用程序。只需添加一点,我们实际上使用了与您建议的处理Dispatcher.UnhandledException事件类似的技术。。。它在那里工作得很好,因为您可以将异常标记为已处理,并且不会进一步传播。AppDomain.UnhandledException不支持此功能,并且总是将异常冒泡到主AppDomain:(你如何在子域中执行代码?你能使用一个助手方法调用代码并捕获任何未处理的异常吗?虽然我们不控制外接程序的功能,但我们控制子应用程序域端的引导过程(我们提供了一个框架供外接程序开发人员构建)…因此,作为引导过程的一部分,我们可以从子AppDomain内钩住Dispatcher.UnhandledException和AppDomain.UnhandledException事件,并在子AppDomain端处理它们。但是,我发现无法阻止AppDomain.UnhandledException异常冒泡到父AppDomain,即使我自己在子域中处理它们。@James Thurley:我的意思是,当您调用外接程序的entrypoint方法时,可以在子域中捕获异常。但是,我刚刚意识到,如果外接程序在其他线程中引发异常,这可能不起作用。如果您真的想确保外接程序不会破坏您的进程,你应该有另一个进程来运行外接程序。嘿,詹姆斯,你找到解决方案了吗?@Joshjje,这不是一个好的解决方案……我们最终只是在每个AppDomain中处理未处理的异常,然后让AppDomain从那里卸载自己。这显然需要信任每个外接程序来设置这个系统,谢天谢地我们可以为我们的应用程序提供支持。但我没有找到从主AppDomain中管理整个过程的方法。
class Program {
    private class NonSerializableException : Exception { }

    static void Main(string[] args) {
        var childAppDomain = AppDomain.CreateDomain("Child");
        Console.WriteLine("Created child AppDomain #{0}.", childAppDomain.Id);

        // I did not create a new assembly for the helper class because I am lazy :)
        var helperAssemblyLocation = typeof(AppDomainHelper).Assembly.Location;
        var helper = (AppDomainHelper)childAppDomain.CreateInstanceFromAndUnwrap(
                helperAssemblyLocation, typeof(AppDomainHelper).FullName);
        helper.Initialize(UnloadHelper.Instance);

        childAppDomain.DoCallBack(
            () => new Thread(delegate() { throw new NonSerializableException(); }).Start());

        Console.ReadLine();
    }

    private sealed class UnloadHelper : MarshalByRefObject, IAppDomainUnloader {
        public static readonly UnloadHelper Instance = new UnloadHelper();

        private UnloadHelper() { }

        public override object InitializeLifetimeService() {
            return null;
        }

        public void RequestUnload(int id) {
            // Add application domain identified by id into unload queue.
            Console.WriteLine("AppDomain #{0} requests unload.", id);
        }
    }
}

// These two types could be in another helper assembly

public interface IAppDomainUnloader {
    void RequestUnload(int id);
}

public sealed class AppDomainHelper : MarshalByRefObject {
    public void Initialize(IAppDomainUnloader unloader) {
        AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
                unloader.RequestUnload(AppDomain.CurrentDomain.Id);
    }
}