【问题标题】:Showing stack trace of exception that is re-thrown, rather than stack trace from throw point显示重新抛出的异常的堆栈跟踪,而不是来自抛出点的堆栈跟踪
【发布时间】:2009-09-03 06:05:09
【问题描述】:

我已经在 VS2005 中确认了同样的行为,因此将其称为 .NET (1.1) 错误是错误的。

我将在下面留下原来的问题,但我修改后的问题是:如何让 Visual Studio 给我在调用堆栈窗口中捕获并重新抛出的异常的堆栈跟踪 ,而不是只显示从throw 语句开始的调用堆栈?

情况是我决定 在运行时 全局异常处理程序是打开还是关闭 - 如果它关闭,我希望 VS 捕获异常,以便我可以通过调用堆栈单步执行找出问题所在。

以前,全局异常处理程序要么编译到程序中,要么不编译。但是情况发生了变化,现在我们需要在运行时做出决定——看起来我可能需要回到使用宏的方式来做这件事,但没有宏:

if (allow_bubble_up)
{
    Foo();
}
else
{
    try
    {
        Foo();
    }
    catch (Exception e)
    {
       GlobalExceptionHandler(e);
    }
}

但对我来说,这种方法对 DRY 的感觉非常


显然 .NET 1.1 中存在一个错误,如果您有一个空的 throw 语句来重新引发捕获的异常,则堆栈跟踪将从发生 throw 的位置开始,而不是整个异常被重新抛出 - 至少,我在几个博客上看到它称为错误,但我无法获得更多关于它的信息。

更具体一点,QuickWatch 中$exceptionStackTrace 属性显示了正确的数据,但VS 中的Call Stack 窗口只显示了throw 语句级别的调用堆栈。

在此示例代码中,我只能看到 Main 的 1 级深度堆栈跟踪,尽管我应该看到对 Foo 的几次调用的堆栈跟踪。

static public void Foo(int i)
{
    if (i > 4)
    {
        throw new ArgumentOutOfRangeException();
    }
    Foo(i + 1);
}

static void Main(string[] args)
{
    bool allow_bubble_up = true;
    try
    {
        Foo(0);
    }
    catch (Exception e)
    {
        if (allow_bubble_up)
        {
            // stack trace just shows Main
            throw;

            // also just shows Main
            //throw new Exception("asdf", e);

            // STILL just shows Main
            //throw e;
        }
        else
        {
            System.Console.WriteLine(e);
        }
    }
}

Fabrice Marguerie's blog 展示了如何解决 .NET 2.0+ 的某种重新抛出的堆栈跟踪,在底部他说查看 Chris Taylor 的博客以了解如何在 .NET 1.1 中执行此操作。我不得不搜索一下find it on archive.org。我认为我正确地实现了它,但我仍然在 main 处得到一个堆栈跟踪——他的解释不是很清楚,我不想弄乱代码库(包装现有集其他方法中的功能)超过必要。

我可以在捕获和重新引发的异常的属性中看到正确的堆栈跟踪,但是 VS 显示的可导航堆栈跟踪是无用的,因为它只跟踪 throw 语句。如果我从不捕获并重新抛出异常,我确实会获得完整且正确的堆栈跟踪。

如何在 VS 中显示正确的堆栈跟踪? 我希望有某种简单的解决方法,但我一直在搜索错误的术语。

不幸的是,它必须是 VS2003+C#。

如果不清楚,这里是截图(您可能需要右键单击并查看图像):

alt text http://img257.imageshack.us/img257/1124/40727627.png

【问题讨论】:

  • 我记得这对于 1.1 非常令人沮丧。当时从未寻求过解决方案,但你并不疯狂。我知道你的意思!但并不像我想象的还要使用 1.1 那样令人沮丧!
  • 这看起来是正确的,因为 Main 是 Foo 的调用者,它最终抛出了异常。
  • @Mark Rushakoff:我已经强调了调用堆栈窗口点,这是最大的混淆点。

标签: c# visual-studio stack-trace


【解决方案1】:

Visual Studio 将显示它停止的地方的调用堆栈。

未处理异常的情况下,它将在抛出该异常的地方停止。即您的“投掷”声明。但是,如果您的代码处理异常,则 Visual Studio 会假定您知道自己在做什么,并忽略该异常。它只会在 Main() 中重新抛出异常时捕获异常,因为您没有在程序中处理它。

如果您想在 Visual Studio 中捕获原始异常,您有两种选择:

  • 不要在代码中捕获异常。默认情况下,Visual Studio 只会在 未处理 异常处停止。这当然意味着你的程序不会在运行时处理异常,所以这不是一个非常有用的方法!

  • 使用您的代码来捕获并重新引发异常(正如您所做的那样),但将 Visual Studio 配置为在首次引发内部异常时停止。转到调试>异常并勾选“公共语言运行时”异常框(停止任何异常)或浏览子树以启用特定异常的异常捕获(提示:如果您知道异常名称,请点击查找...按钮并输入部分名称,例如“FileNotFound”,以快速查找异常)。这将使 VS 在内部异常处停止,并且只有在检查异常详细信息后选择继续执行时才继续执行 catch{} 语句。

【讨论】:

  • 这就是答案。精通。纯粹是高手。
【解决方案2】:

您可以抛出一个新异常,将异常 e 作为内部异常。然后读取内部异常的stacktrace。

【讨论】:

  • 我可以将堆栈跟踪作为字符串读取就好了,但是当我需要浏览调用堆栈以找出问题所在时,我别无选择,只能手动查找文件和行调用堆栈中的每一项。
  • 那么 Visual Studio 中缺少的应该是什么?我想这是我的问题。
【解决方案3】:

事实证明,如果您知道要搜索的正确术语,就是我试图解决的问题的答案。在 MSIL 中称为异常过滤it is available in VS2003

在 Visual Basic.NET 中,有一个称为“catch-when”的结构,它只会在给定谓词通过时执行 catch。 This MSDN blog 有一个很好的例子,说明了 VB.NET 中的 catch-when 与 C# 的 catch-throw 的结果(就像我的一样)。

最后,MSDN 有一个名为 Exception Filter Inject 的工具,可用于“为不支持异常过滤器的语言(例如 C#)提供异常过滤器支持”——关键是它在现有程序集上运行,所以如果你最终使用它,它确实会在构建过程中引入一个尴尬的阶段。


在我找到异常过滤器注入之前,我最终实现了一个短函数,它接受一个“功能”委托和一个“捕获”委托,如果允许异常冒泡,则只调用函数,否则调用函数一个 try-catch,在捕获的异常上调用 catch 委托。

想要做的,以某种方式引导我找到异常过滤,是能够设置异常的类型以在运行时捕获 - 如果异常应该冒泡,我会尝试捕获一个永远不会被调用的子类异常,否则我只会捕获一个基本异常。我真的不确定这在 .NET 1.1 中是否可行,因为这基本上需要一个泛型——但反射可能是可能的,我只是在我的研究中从未走得那么远。

【讨论】:

    【解决方案4】:

    如果我理解您的消息,那么在正确的错误堆栈跟踪与特定时间点的当前调用堆栈(不是你想要什么)

    然而,一旦你进入你的异常处理例程,Foo 例程已经完成,所以我看不出它是如何成为你当前调用堆栈的一部分的。

    除了启用 'break at first exception' 之外,我看不到它是如何工作的,并且不知道 VS2003 或 VS2005 中的任何内容会对此有所帮助。 (也许是 VS2010 中新的调试/回放功能)

    【讨论】:

    • 对于重新抛出的异常的堆栈跟踪存在混淆(主要/全部是我自己)。异常的 StackTrace 属性是正确的,但我想让 VS 中断并允许我在调用堆栈中导航,就好像我根本没有捕获到异常一样。不过,我理解您关于 Foo 已完成且临时变量不再存在的观点。正如我在问题顶部的编辑中提到的那样,我可能不得不将 try-catch 放在 if 语句的单独分支中。如果有人知道另一种方法,请保持开放状态。
    【解决方案5】:

    您所描述的是调用堆栈窗口的预期行为。当 Visual Studio 由于 UnhandledException 而在 throw 行中断时,调用堆栈从 throw 行开始显示是正确的。

    归结为 Visual Studio 的调用堆栈窗口不知道异常中包含的堆栈跟踪。

    【讨论】:

    • +1,我错误地认为空的throw 会表现得好像我根本没有发现异常。我把这个问题留了一会儿,看看是否有人确实知道一种方法可以欺骗 VS 以这种方式运行,否则我可能会接受你的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    • 2011-05-12
    • 1970-01-01
    • 1970-01-01
    • 2011-09-10
    相关资源
    最近更新 更多