如何抓住StackOverflowException
?
我有一个允许用户编写脚本的程序,当运行任意用户代码时,我可能会得到StackOverflowException
。运行用户代码的部分显然被try
- catch
包围,但在正常情况下堆栈溢出是无法捕获的。
我环顾四周,this是我能找到的最具信息性的答案,但仍然让我陷入了死胡同;从article in the BCL team's blog我发现我应该使用RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup
来调用代码和即使在堆栈溢出后也会被调用的委托,但是在尝试时,进程将终止,堆栈溢出消息没有代表被召唤。我尝试在处理程序方法上添加PrePrepareMethodAttribute
,但这并没有改变任何东西。
我也尝试过使用AppDomain并处理UnhandledException
和DomainUnload
事件 - 但整个进程在堆栈溢出时被终止。即使我手动throw new StackOverflowException();
并且没有得到实际的堆栈溢出,也会发生同样的情况。
答案 0 :(得分:2)
要处理代码未处理的异常,您可以订阅AppDomains UnhandledException - 这是操作系统在显示意外退出程序的对话框时处理的内容。
在程序的Main方法中使用
var currentDomain = AppDomain.CurrentDomain;
然后为事件添加处理程序
currentDomain.UnhandledException + = handler;
在处理程序中,您可以执行任何操作,例如日志,显示错误,甚至根据需要重新初始化程序。
答案 1 :(得分:2)
编写脚本引擎以跟踪脚本中的递归级别。如果递归超过某个任意大的数字,那么在它杀死你的程序之前杀死脚本。或者,您可以将脚本引擎编程为以无堆栈方式操作,并将所有脚本的堆栈数据存储在System.Collections.Generic.Stack< T>中。即使您使用单独的堆栈,您仍然希望限制脚本可以具有的递归级别,但堆栈集合将为您提供几百倍的堆栈空间。
答案 2 :(得分:0)
您需要在单独的过程中运行代码。
答案 3 :(得分: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的同事。