2011-03-28 112 views
6

非序列未处理的异常,我们正在使用System.AddIn到插件加载到单独的AppDomain的孩子打交道,我们卸载加载的AppDomain中,如果有未处理的异常。从一个孩子的AppDomain

由于版本2.0的.NET的默认行为是拆除整个过程,如果有任何AppDomain有未处理的异常,我们通过使用我们的App.config中的“legacyUnhandledExceptionPolicy”选项,然后手动拆除如果未处理的异常处于主AppDomain中,则进行处理;如果处于加载项中,则卸载相应的AppDomain。

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

相反,我们得到一个SerializationException出现在主AppDomain中的UnhandledException,导致我们的应用程序本身推倒。

我能想到这个问题的一些可能的解决方案:

  • 我们不能推倒未处理SerializationExceptions(呸)的过程。

  • 我们可以从孩子的AppDomain传播到主要的AppDomain停止例外。

  • 我们可以更换序列化的,也许使用序列代理人和系列化粘合剂的不可序列化的例外。 [编辑:看到最后为什么这是不可能的使用代理人]

然而,第一个是很可怕的,我已经搞清楚怎么做任何其他选项有交叉不成功AppDomain远程处理。

任何人都可以提供一些建议?任何帮助表示赞赏。

要重现创建具有以下的App.config控制台应用程序:

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

和以下的代码:

class Program 
{ 
    private class NonSerializableException : Exception 
    { 
    } 

    static void Main(string[] args) 
    { 
     AppDomain.CurrentDomain.UnhandledException += MainDomain_UnhandledException; 

     AppDomain childAppDomain = AppDomain.CreateDomain("Child"); 
     childAppDomain.UnhandledException += ChildAppDomain_UnhandledException; 

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

     Console.ReadLine(); 
    } 

    static void MainDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
    { 
     Console.WriteLine("Main AppDomain Unhandled Exception: " + e.ExceptionObject); 
     Console.WriteLine(); 
    } 

    static void ChildAppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
    { 
     Console.WriteLine("Child AppDomain Unhandled Exception: " + e.ExceptionObject); 
     Console.WriteLine(); 
    } 
} 



编辑:我使用反射器以查看是否有任何方法可以访问跨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); 
} 

因此,该方法在本地创建的格式,并明确是没有办法自己带上代孕...我认为这使得任何朝这个方向作更多的努力是徒劳的。

+0

嗨詹姆斯,你有没有找到一个解决这个? – Joshjje 2013-11-22 20:06:22

+0

@Joshjje,不是一个好的解决方案......我们最终只是处理每个AppDomain中的未处理的异常,然后让AppDomain从那里卸载它自己。这显然需要信任每个加载项来设置此系统,谢天谢地,我们可以为我们的应用程序。虽然我没有找到一种方法来管理主AppDomain内的整个流程。 – 2013-12-23 14:16:40

回答

2

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

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

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); 
    } 
} 
+0

感谢mgronber,但即使使用此解决方案,NonSerializableException仍会作为未处理的SerializationException传播到父AppDomain。我无法将SerializationException从子AppDomain中识别出来,这迫使我忽略它,或将它视为任何其他未处理的异常并终止该应用程序。 – 2011-03-30 11:03:57

+0

为了增加,我们实际上使用了一种类似的技术来处理Dispatcher.UnhandledException事件......它在那里效果很好,因为您可以将异常标记为已处理,并且不会进一步传播。 AppDomain.UnhandledException可悲地不支持这一点,并总是将异常冒泡到主AppDomain :( – 2011-03-30 11:07:39

+0

如何执行子域中的代码?您可以使用一个助手方法来调用代码并捕获所有剩下的异常未处理? – mgronber 2011-03-30 13:39:51