AppDomain未处理异常

5

有很多关于这个问题的主题。但是我还是有一个问题。

我像这样将程序集加载到新的AppDomain中:

public void Run()
{
    //There's the problem.
    //As Panos Rontogiannis mentioned the thread is created in default AppDomain
    new Thread(RunApp).Start();
}

private void RunApp()
    try
    {
        AppDomain.CreateDomain("domain name").ExecuteAssembly("path to assembly");
    }
    catch (Exception _e)
    {
        MessageBox.Show("Unhandled Exception.\n" + _e);
    }
}

在加载程序集的主方法中,我将处理程序订阅到 UnhandledException 事件:
AppDomain.CurrentDomain.UnhandledException += handleException;

处理程序本身:
public static void handleException(object a_s, UnhandledExceptionEventArgs a_args)
{
    var _e = (Exception)a_args.ExceptionObject;
    //Static loger class method
    Loger.WriteError(_e.GetType().ToString(), _e.Message, "default solution");
}

但是,在加载的程序集中抛出异常时,处理程序并不参与其中。 我只在默认的AppDomain(第一个try{} catch{})中捕获异常。
6个回答

4
很可能,您无法在新的AppDomain中处理异常的原因是它不是从在该AppDomain中创建的线程抛出的。从AppDomain.UnhandledException的文档中,这并不是非常直观的。有趣的部分如下所示:
仅当线程的整个堆栈在未找到适用的异常处理程序的情况下展开时,异常才无法处理,因此可以在引发事件的第一个位置上升到线程发源地的应用程序域。
如果执行抛出代码的线程是在您的主AppDomain(如控制台应用程序的主线程)中创建的,则应在主AppDomain中添加处理程序。请注意,如果抛出异常的类型未在主要AppDomain中加载,则.NET的程序集加载器将尝试从应用程序的基目录和探测路径加载它。如果这些与子AppDomain不同,则程序集将无法解决并抛出另一个异常。

你是对的。ExecuteAssembly 在默认 AppDomain 中创建的新 Thread 中被调用。但是,在新的 AppDomain 中加载的程序集内部处理异常非常重要。 - wazelin
@Wazelin 你所说的处理,是指简单地记录它。对于未处理的异常,你没有太多其他选择。如果未处理的异常是从子应用程序域中创建的线程抛出的,那么该应用程序域和应用程序都将受到影响。如果你想将你的应用程序与其他代码隔离开来,你需要使用不同的进程。 - Panos Rontogiannis

1

在我看来,这个答案应该是一条评论。 - Kiquenet

0
这是一个晚回复,但我认为这个方法很好用(VS2012/.NET 4.5),当然在调用ExecuteAssembly之前需要注册异常处理程序:(我有一个子进程,通过写入空引用(不安全代码)来强制崩溃并触发下面的HandleException:
public static void HandleException(object a_s, UnhandledExceptionEventArgs a_args)
{
    var _e = (Exception)a_args.ExceptionObject;
    Console.WriteLine(_e.GetType().ToString(), _e.Message, "default solution");
}

public void StarProcessWithinAppDomain(string fileName)
{
    try
    {
        // New appdoamin / check exception isolation level 
        AppDomain sandBox = AppDomain.CreateDomain("sandBox");
        try
        {
            AppDomain.CurrentDomain.UnhandledException += HandleException;
            sandBox.ExecuteAssembly(fileName);
        }
        catch (Exception ex)
        {
            Console.WriteLine("An error occurred (inner) within AppDomain, executing \"{0}\":" + "\n" + ex.Message, fileName);
        }
        finally
        {
            AppDomain.Unload(sandBox); 
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("An error occurred within AppDomain, executing \"{0}\":" + "\n" + ex.Message, fileName);
    }
}

0
  1. 触发 FirstChanceException
  2. 执行任何 catch 块。
  3. 如果没有 catch 块或块中没有 throw,则会触发 UnhandledException

您的 catch 块确保不会触发 UnhandledException


0

我的猜测是处理程序没有被调用,因为异常已经被处理了。也就是说,被上层的try{}catch{}捕获了。


0

我真的不明白为什么处理程序没有被调用。

最终,我在已加载程序集的Main方法中使用了FirstChanceException而不是UnhandledException。像这样:

AppDomain.CurrentDomain.FirstChanceException +=
    (obj, e) => Loger.WriteError(
        e.Exception.ToString(),
        e.Exception.Message,
        "default solution"
    );

可能适合你的目的,但我怀疑。第一次机会异常事件与未捕获异常不同。简而言之,您将为每个引发的异常获取此事件,无论它是否最终未被代码处理(没有重新抛出的catch块) 。 - Christian.K
既然我只是将其用于记录目的,我认为这样做没问题。异常被记录下来,如果存在处理程序,则它们会继续执行它们的工作。 - wazelin
是的,但请记住,一些代码(甚至在 .net 框架本身中)会在内部使用异常,这些异常永远不会出现,并可能用“虚假”的异常混淆您的日志记录。当然,您的情况可能有所不同。 - Christian.K
谢谢你的提醒。我没想过这个问题。 - wazelin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接