2010-08-29 38 views
6

如何捕捉StackOverflowException捕捉一个StackOverflowException

我有一个程序,允许用户编写脚本,当运行任意用户代码时,我可能会得到StackOverflowException。片段运行用户代码显然被一个try - catch包围,但在正常情况下堆栈溢出不可捕获。

我环顾四周,this是我能找到的最丰富的答案,但仍使我陷入死胡同;从article in the BCL team's blog我发现我应该使用RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup调用即使在堆栈溢出后也会调用的代码和委托,但在尝试时,进程会因堆栈溢出消息而终止,而不会调用委托。我已经尝试在处理程序方法中添加PrePrepareMethodAttribute,但这并没有改变任何内容。

我也尝试过使用AppDomain并处理UnhandledExceptionDomainUnload事件 - 但是整个过程在堆栈溢出时死亡。即使手动输入throw new StackOverflowException();也不会发生实际的堆栈溢出。

+0

你有一个程序可以使用脚本来自动化一些任意的过程,或者你的程序是一个脚本的文本编辑器?你的“任意用户代码”本身是一个脚本还是某种IL插件? – 2010-08-30 06:45:32

+0

现在我的程序是语言的REPL(read-eval-print loop)。 – configurator 2010-08-31 10:38:15

回答

0

您需要在单独的进程中运行代码。

+1

然后用户仍然会看到进程崩溃,包括在这些情况下发生的令人讨厌的Windows屏幕。我如何从父进程中检测到这种情况以防止这种情况发生? – configurator 2010-08-29 17:35:44

2

要处理未由您的代码处理的异常,您可以订阅AppDomains UnhandledException - 这是操作系统在显示程序意外退出的对话框时处理的内容。

在你的程序的主要方法使用

变种currentDomain = AppDomain.CurrentDomain;

,然后添加处理程序到事件

currentDomain.UnhandledException + =处理程序;

在处理程序中,您可以执行任何您想要的操作,如日志,显示错误或者根据需要重新初始化程序。

+0

你是对的,那是我尝试的第一件事 - 但它没有奏效。我已经添加了这个问题。 – configurator 2010-08-29 18:01:35

2

编写脚本引擎来跟踪脚本中的递归级别。如果递归超过一些任意大的数字,则在杀死程序之前杀死该脚本。或者,您可以将脚本引擎编程为以无堆栈方式运行,并将所有脚本的堆栈数据存储在System.Collections.Generic.Stack <T>中。即使您使用单独的堆栈,您仍然希望限制脚本可以具有的递归级别,但堆栈收集会为您提供几百倍的堆栈空间。

+0

这听起来像他正在运行原始IL,而不是他自己的翻译。 – SLaks 2010-08-30 02:48:41

+0

我其实是在经营我自己的口译员。但是,如果可能的话,我想要一个适用于原始IL的解决方案。 – configurator 2010-08-31 22:32:35

+0

此外,脚本可以调用任何.net函数 - 这意味着我无法很好地保护呼叫路径。 – configurator 2010-09-03 10:02:41

0

您必须将用户脚本或任何外部第三方插件加载到不同的应用程序域中,以便在发生不可恢复的错误时安全地卸载域。

您必须创建一个不同的AppDomain,因为您无法从加载的域中卸载程序集,也不想关闭主应用程序域。

您创建一个新的应用领域是这样的:

var scriptDomain = AppDomain.CreateDomain("User Scripts"); 

然后,您可以从您需要创建一个集加载任何类型。您必须确定您要加载的对象从MarshalByRefObject继承。

我假设你的用户脚本被包裹这样定义的对象内:

public abstract UserScriptBase : MarshaByRefObject 
{ 
    public abstract void Execute(); 
} 

可以因此加载任何这样的用户脚本:

object script = domain.CreateInstanceFromAndUnwrap(type.Location, type.FullName); 

毕竟,你可以订阅到scriptDomain.UnhandledException并监控任何不可恢复的错误。

使用不同的应用程序域并不容易,您很可能会遇到一些加载/卸载问题(DLL由两个域引用)。

我推荐你在网上找到的伙伴some tutorial

+2

这不会帮助。 'StackOverflowException'终止整个过程,不管它发生在哪个'AppDomain'。 – 2010-08-29 19:50:47

+0

我同意,这是一个好主意,但不幸的是它不起作用... – configurator 2010-08-29 20:34:36

+0

@configurator,它是一个好主意如果它不起作用?也许它很聪明或复杂,但不是很好。 – JMCF125 2013-03-27 19:23:37