C# 异常处理循环难题
我最近遇到了一个我从未见过的行为。我不能完全理解发生了什么,很可能是因为缺乏关于内部工作异常处理的基本知识——或者我只是缺少了一些明显的东西 我最近在一个应用程序中添加了异常处理,作为未处理异常的一种回退。我基本上是在处理ThreadException和UnhandleException,如下所示:C# 异常处理循环难题,c#,.net,exception,exception-handling,C#,.net,Exception,Exception Handling,我最近遇到了一个我从未见过的行为。我不能完全理解发生了什么,很可能是因为缺乏关于内部工作异常处理的基本知识——或者我只是缺少了一些明显的东西 我最近在一个应用程序中添加了异常处理,作为未处理异常的一种回退。我基本上是在处理ThreadException和UnhandleException,如下所示: // Add the event handler for handling UI thread exceptions to the event. Application.ThreadExceptio
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(ExceptionHandler.OnUIThreadException);
// Set the unhandled exception mode to force all Windows Forms errors to go through
// our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(ExceptionHandler.OnUnhandledException);
// Runs the application.
Application.Run(new ErrorHandlerForm());
我在应用程序中的另一段代码已经捕获了异常——由于我没有进行异常处理,我只是重新调用异常以确保它不会被吞没:
//code in some method of the Program
try
{
foo.SomeFooCall();
}
catch(Exception ex)
{
logger.Log(ex.Message);
// I don't swallow!
throw;
}
一旦我有了异常处理(这也是日志记录),我应该删除上面的try-catch块——但是我忘记了这么做,我遇到了一个奇怪的行为,这就是这个问题的主题
当在foo调用中的某个地方抛出and exception时,显然会被上面的代码捕获,记录下来,然后再次抛出。此时ExceptionHandling启动,执行一些日志记录和通知(一个简单的消息框),然后转到Application.Exit()
。接下来的情况是,应用程序将返回相同的throw
,这将触发具有相同结果的错误处理,并且这将持续多次,直到崩溃,可能是因为堆栈跟踪已满或检测到无限循环。
编辑:以上是调试模式-如果我只是运行它,它将处理一次异常(显示messagebox、日志等),然后它将崩溃(我猜是堆栈溢出)。
我预计这个问题的答案可能很琐碎(或者我可能遗漏了一些明显的东西),但任何提示/解释都将受到高度赞赏
编辑:
异常处理程序方法将这两个调用都向下调用到OneException方法,如下所示:
private void OnUIThreadException(object sender, ThreadExceptionEventArgs e)
{
OnException(e.Exception);
}
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
OnException((Exception)e.ExceptionObject);
}
private void OnException(Exception exception)
{
MessageBox.Show("Fatal Exception: " + exception.Message);
logger.Log(LoggingLevel.FATAL, "myLousyApp", exception.Message);
Application.Exit();
}
实际上,我做的smt不仅仅是这些——比如询问用户是否想重新启动应用程序,如果是这样,则使用进程id作为cmd arg重新启动应用程序,这样当它重新启动时,它将等待旧进程退出(通过互斥锁保护它不受重复实例的影响)。但对于这个问题,这是无关的,因为当我体验到所描述的行为时,我没有重新启动应用程序
编辑:我创建了另一个简单的应用程序来重现这种情况-我有一个简单的组件可以抛出异常(我在一个循环中抛出任意数量的异常),但在我对应用程序的所有测试中。退出应用程序只会很好地关闭,我无法重现它。对我应该寻找的东西感到困惑 我怀疑在
ExceptionHandler.OnUnhandledException
中可能会引发一个新的异常,该异常保持未处理状态,并冒泡到未处理的异常处理程序,从而引发您的ExceptionHandler.OnUnhandledException
方法等等。。(无限循环->堆栈溢出)
因此,请仔细检查OnUnhandledException方法是否存在抛出的错误
编辑:有趣的是,我试图重现你的错误,但不是一次又一次地抛出错误,而是我的应用程序。只要在未处理的异常处理程序方法之后退出,不管其中发生了什么。你是在调试还是在运行?这可能会造成不同。如果不知道原始抛出的代码在做什么,很难判断。可能存在未完成的表单事件,这些事件作为应用程序关闭的一部分进行处理,最终重新调用原始代码片段。它也可以是应用程序类上的一个虚拟函数,在关闭期间被调用
不过,您应该能够从catch处理程序中打印堆栈跟踪来找出它。tl;dr:是调试器。分离,你就不会有这种奇怪的行为
好的。我对一个全新的项目做了一些实验,得出了一个结果。我将首先发布代码,这样您也可以参与其中,并直接看到它 德科兹 (无关) 表格1.cs
表单上需要两个按钮。适当地给他们加上标题,这样你所做的事情就显而易见了
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
throw new InvalidOperationException("Exception thrown from UI thread");
}
private void button2_Click(object sender, EventArgs e)
{
new Thread(new ThreadStart(ThrowThreadStart)).Start();
}
private static void ThrowThreadStart()
{
throw new InvalidOperationException("Exception thrown from background thread");
}
}
Program.cs
项目文件
禁用宿主进程,否则AppDomain(和表单本身)将不会在调试会话之间卸载,这将使行Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException,false)如果在两次运行之间更改UnhandledExceptionMode
参数,则code>抛出一个InvalidOperationException
。编辑:如果设置为CatchException,则完全编辑
对于本次调查来说,这并不是绝对必要的,但如果您打算改变未处理的异常模式
——我想如果您自己运行此代码,您可能会这样做——此设置会让您省心。
测试
在调试器内部
加入UI线程
在UI中单击抛出
在调试器中获取“未处理的异常”帮助程序
F5
继续执行
对话框将显示,指示应用程序处理程序从UI线程接收到异常事件
单击“确定”
应用程序不会崩溃,所以可以随意冲洗和重复
加入背景线
单击“在背景中加入”
对话框将显示,指示AppDomain处理程序从后台线程接收到异常事件
在调试器中获取“未处理的异常”帮助程序
F5
继续执行
转到2
。真的
在这里,AppDomain处理程序似乎以任何理由胜过调试器。但是,在AppDomain处理完它之后,调试器会设法发现未处理的异常。但接下来发生的事情令人费解:AppDomain处理程序再次运行。一次又一次。再一次,无限。放br
static class Program
{
[STAThread]
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException, false);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
if (e.Exception != null)
{
MessageBox.Show(string.Format("+++ Application.ThreadException: {0}", e.Exception.Message));
}
else
{
MessageBox.Show("Thread exception event fired, but object was not an exception");
}
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
if (ex != null)
{
MessageBox.Show(string.Format("*** AppDomain.UnhandledException: {0}", ex.Message));
}
else
{
MessageBox.Show("Unhandled exception event fired, but object was not an exception");
}
}
}