c# - 异常调用堆栈被截断而没有任何重新抛出

标签 c# .net exception stack-trace stack-frame

我有一个不寻常的案例,我有一个非常简单的异常被抛出并被同一个方法捕获。 它不会被重新抛出(天真的程序员通常会遇到的那种问题)。然而它的 StackFrame 只包含一个当前方法。这是它的样子:

   at (my class).MyMethod() in C:\(my file path and line)

实际上,在 VS2010 调试器的调用堆栈中可能有 30 种方法导致这种情况,跨越六个不同的程序集。似乎不可能将所有这些都优化掉。此外,这段代码是在 Debug模式下构建的,没有优化,适用于.NET 4。我什至有(基于http://msdn.microsoft.com/en-us/library/9dd8z24x.aspx).ini文件(包括一个名为[app].vshost.ini的文件)在包含以下内容的同一文件夹中:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

此外,方法调用不在方法的末尾,因此尾递归优化似乎更不可能。

至于它的调用方式:在调用堆栈上没有使用反射,没有任何类型的 Invoke() 或 BeginInvoke()。这只是来自单击按钮的一长串调用。单击处理程序在调用堆栈中大约有 10 个调用。在其下,您有常用的 WndProc、NativeWindow.Callback、 native /托管转换和消息循环。这最终位于从 C# EXE 程序集运行的 ShowDialog() 调用中。

现在,我发现我可以在我的 catch 处理程序中构造 StackTrace 类的实例,如果我传递 Exception 对象,调用堆栈也很短。相反,如果我只是调用不带参数的 new StackTrace(),它会产生一个完整的调用堆栈。

我曾使用 Reflector 尝试调试被抛出的 Exception 类的内部结构及其调用堆栈的构造,但我无法在 Exception 或 StackTrace 中设置断点。我可以在 Environment.GetStackTrace() 中设置它们,并且在构建和抛出过程中似乎没有调用此方法(Exception 调用),但我不知道调试器是否真的正常工作。 (虽然这个方法确实会因其他一些事情而被触发,所以我不确定该怎么做。)

这是该方法的摘录:

private void MyMethod()
{
    ...               
    try
    {
        throw new ApplicationException("Test failure");
    }
    catch (Exception e)
    {
        StackTrace stackTrace1 = new StackTrace(e);
        StackTrace stackTrace2 = new StackTrace(e, false);
        StackTrace stackTrace3 = new StackTrace(e, true);
        StackTrace stackTrace4 = new StackTrace();
        string STs = stackTrace1.ToString() + "\n---\n"
            + stackTrace2.ToString() + "\n---\n"
            + stackTrace3.ToString() + "\n---\n"
            + stackTrace4.ToString();
        Log(EventSeverity.Debug, STs);
        ...
        }
    }

这真的很简单:抛出异常,捕获并记录它。

无论是在调试器中还是在独立运行时,我都得到了相同的结果——单行调用堆栈。而且我知道我在我们的代码库的其他地方看到过这个问题。以前我认为这是由于重新抛出异常,但在很多情况下,我们直接在初始 catch block 中记录。我很困惑,我所做的所有网络搜索都没有产生任何结果。


作为评论添加到所提供的答案有点太多了,但这里有一些更多信息:

我现在看到这个行为在 http://dotnetthoughts.wordpress.com/2007/10/27/where-did-my-exception-occur/并且它实际上在 http://msdn.microsoft.com/en-us/library/system.exception.stacktrace.aspx 中进行了描述(尽管我认为人们很容易错过他们在那里说的内容)。

所以我想我的“解决方案”会有点失败。我们有一个我们通常调用的中央方法来格式化异常。在该方法中,我将创建一个新的 StackTrace(),包括和不包括 Exception 对象。然后我将查找位于异常堆栈跟踪底部的方法,并在新的 StackTrace() 中显示该方法下方的所有内容,表明它是由那一系列调用调用的。

当然,不利的一面是,如果不使用此方法,则不会有信息。但我不得不预料到某处会发生某种代码更改。

最佳答案

当抛出异常时,Exception.StackTrace 属性中将仅使用部分堆栈跟踪。堆栈仅显示调用直到捕获异常的方法。要获得完整的堆栈(正如您所指出的),您应该创建一个 new StackTrace() 对象。

目前我找不到任何关于它的链接,但我相信堆栈跟踪是通过在抛出异常时向上遍历堆栈构建的。一旦异常到达 catch block ,堆栈就会停止编译。因此,您只能获得部分堆栈。

通常,catch block 不关心调用它的人,而是关心异常源自何处。

关于c# - 异常调用堆栈被截断而没有任何重新抛出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5301535/

相关文章:

c# - 哪种数据类型最适合 C++ 中 Thrift 通信中的日期时间?

.net - 确定 System.AccessViolationException 的原因

java - 如果传入无效值则抛出异常

java - Android - 当定义了多个按钮时,自定义对话框抛出 NullPointerException

c++ - 从 ASSERT_THROW 获取异常信息

c# - 使用 SummaryRow 和分组时 Syncfusion SfDataGrid 崩溃

c# - 什么是 C# 中的 Bootstrappers/Bootstrapping

c# - AppDomain.CreateInstanceAndUnwrap 可以返回 null 吗?

c# - 使用 DrawingContext.DrawText() 在 WPF 中绘制垂直文本

c# - .NET 3.5 和 4.5 中 LINQ 查询结果的差异