c# - 隔离 AppDomain 中抛出的异常,以免应用程序崩溃

标签 c# .net appdomain

TL;DR:如何将加载项异常与终止主进程隔离开来?

我想要一个非常稳定的 .Net 应用程序,它在 AppDomain 中运行不太稳定的代码。这似乎首先是 AppDomain 的主要目的之一(以及安全沙箱),但它似乎不起作用。

例如在 AddIn.exe 中:

public static class Program
{
    public static void Main(string[] args)
    {
        throw new Exception("test")
    }
}

在我的“稳定”代码中调用:

var domain = AppDomain.CreateDomain("sandbox");
domain.UnhandledException += (sender, e) => { 
    Console.WriteLine("\r\n ## Unhandled: " + ((Exception) e.ExceptionObject).Message);
};
domain.ExecuteAssemblyByName("AddIn.exe", "arg A", "arg B")

AppDomain 中抛出的异常被直接传递给创建该域的应用程序。我可以使用 domain.UnhandledException 记录这些并在包装器应用程序中捕获它们。

但是,抛出更多有问题的异常,例如:

public static class Program
{
    public static void Main(string[] args)
    {
        Stackoverflow(1);
    }

    static int Stackoverflow(int x)
    {
        return Stackoverflow(++x);
    }
}

这将抛出一个 stackoverflow 异常,每次都会杀死整个应用程序。它甚至不会触发 domain.UnhandledException - 它会直接杀死整个应用程序。

此外,从 AppDomain 内部调用 Environment.Exit() 也会终止父应用程序,不要通过 GO,不要收取 200 英镑,也不要不要运行任何 ~FinialiserDispose()

从这里看来,AppDomain 根本没有做它声称的(或者至少是它看起来声称的)做的事情,因为它只是将所有异常直接传递给父域,使得它对隔离毫无用处,而且对任何类型的安全性都非常弱(如果我可以删除父进程,我可能会危及机器)。这在 .Net 中将是一个非常根本的失败,所以我的代码中一定遗漏了一些东西。

我错过了什么吗?有什么方法可以使 AppDomain 实际上隔离它正在运行的代码并在发生错误时卸载?我是否使用了错误的东西,还有其他一些 .Net 功能确实提供了异常隔离吗?

最佳答案

我会抛出一些随机想法,但@Will 所说的关于权限、CAS、安全透明度和沙盒的内容是正确的。 AppDomains 不是超人。不过,关于异常,AppDomain 能够处理大多数 未处理的异常。它们不是的异常类别称为异步异常。既然我们有了 async/await,查找关于此类异常的文档就有点困难了,but it exists , 它们以三种常见形式出现:

  • 堆栈溢出异常
  • 内存不足异常
  • 线程中止异常

这些异常被称为异步的,因为它们可以在任何地方抛出,甚至在 CIL 操作码之间。前两个是关于整个环境的死亡。 CLR 缺乏 Phoenix 的能力,它无法处理这些异常,因为这样做的方法已经死了。请注意,这些规则仅在 CLR 抛出它们时才存在。如果您只是 new-up 和实例并自己抛出它,它们的行为就像正常的异常。

Sidenote: If you ever peek at a memory dump of a process that is hosting the CLR, you will see there are always OutOfMemoryException, ThreadAbortException, and StackOverflowException on the heap, but they have no roots you can see, and they never get GCed. What gives? The reason they are there is because the CLR preallocates them - it wouldn't be able to allocate them at the time they are needed. It wouldn't be able to allocate an OutOfMemoryException when we're out of memory.

有一款软件可以处理所有这些异常。从 2005 年开始,SQL 已经能够运行具有称为 SQLCLR 的功能的 .NET 程序集。 SQL Server 是一个相当重要的进程,让 .NET 程序集抛出 OutOfMemoryException 并导致整个 SQL 进程中断似乎非常不受欢迎,因此 SQL 团队不允许这种情况发生。

他们使用称为约束执行 和关键区域的 .NET 2.0 功能来做到这一点。这是ExecuteCodeWithGuaranteedCleanup之类的地方参加进来。如果您能够自己托管 CLR,从 native 代码开始并自己启动 CLR,那么您就能够更改升级策略:您能够从 native 代码处理那些托管异常。这就是 SQL CLR 处理这些情况的方式。

关于c# - 隔离 AppDomain 中抛出的异常,以免应用程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30808308/

相关文章:

c# - 在 ASP.NET MVC 中创建具有多个下拉菜单和文本字段的大型表单

c# - 但是,虽然稍后将明文密码分配给密码,但该变量永远不会从内存中清除

c# - 创建的 AppDomain 上的 AssemblyResolve 事件出现问题

c# - 不变性的真正定义?

.net - 在WCF中使用KnownType和ServiceKnownType有什么区别?

c# - 将 DLL 加载到一个单独的 AppDomain 中,只有已知的通用接口(interface)

c# - 在 ASP.NET 中的其他 AppDomain 中运行应用程序

c# - Foreach 没有遍历所有项目?

c# - PdfPTable单元格宽度

c# - 在 C# 中,使用 tick 比较两个日期和按原样比较两个日期有什么区别