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

时间:2011-03-14 16:36:06

标签: c# .net exception stack-trace stackframe

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

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

实际上,在VS2010调试器的调用堆栈中,可能有30种方法可以实现这一点,这可以通过六个不同的程序集来实现。似乎不可能对所有人进行优化。此外,对于.NET 4,此代码以调试模式构建,没有优化。我甚至拥有(基于http://msdn.microsoft.com/en-us/library/9dd8z24x.aspx)。ni文件(包括一个名为[app] .vshost的文件。 ini)在包含以下内容的同一文件夹中:

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

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

关于如何调用它:调用堆栈没有反射的使用,没有任何类型的Invoke()或BeginInvoke()。这只是一个按钮点击的长链调用。点击处理程序大约是调用堆栈的10次调用。在它之下你有通常的WndProc,NativeWindow.Callback,本机/托管转换和消息循环。这最终是在一个从C#EXE程序集运行的ShowDialog()调用中。

现在,我发现我可以在catch处理程序中构造StackTrace类的实例,如果我传递Exception对象,则调用堆栈也很短。相反,如果我只调用没有参数的新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块中记录。我很困惑,我所做的所有网络搜索都没有产生任何东西。


这对于提供的答案添加评论有点过分,但这里有更多信息:

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

所以我猜我的“解决方案”将会有点昙花一现。我们通常会调用一种中心方法来格式化异常。在该方法中,我将使用和不使用Exception对象创建一个新的StackTrace()。然后我将查找位于Exception堆栈跟踪底部的方法,并在新的StackTrace()中显示其下方的所有内容,表明它已被该系列调用调用。

当然,不利的是,如果不使用此方法,信息将不会存在。但我不得不期待在某处改变某种代码。

1 个答案:

答案 0 :(得分:18)

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

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

通常情况下,catch块与谁调用它无关,而是由异常源于何处。