我已经在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
的地方开始,而不是重新抛出整个异常的堆栈跟踪 - 至少,我在一些博客上看到它被称为bug,但我无法获得更多关于它的信息。
更具体一点,QuickWatch中StackTrace
的{{1}}属性显示正确的数据,但VS中的调用堆栈窗口仅显示调用堆栈到throw语句的级别。
在这个示例代码中,我只能看到$exception
的1级深度堆栈跟踪,即使我应该看到对{{1}的几个调用的堆栈跟踪}}
Main
Fabrice Marguerie's blog展示了如何解决.NET 2.0+的某些类型的重新抛出的堆栈跟踪,并在底部他说要检查Chris Taylor的博客,了解如何在.NET 1.1中执行此操作。我不得不搜索一下find it on archive.org。我认为我正确地实现了它,但我仍然在主要处有一个堆栈跟踪 - 他的解释并不十分清楚,我宁愿不要乱用代码库(包装现有的集合)在另一种方法中的功能)不仅仅是必要的。
我可以在捕获和重新引发的异常的属性中看到正确的堆栈跟踪,但是VS显示的可导航堆栈跟踪是无用的,因为它只跟踪Foo
语句。如果我从未捕获并重新抛出异常,我做获得完整且正确的堆栈跟踪。
如何在VS中显示正确的堆栈跟踪? 我希望有一些简单的解决方法,而且我一直在寻找错误的术语。
不幸的是,它必须是VS2003 + C#。
如果不清楚,这里是截图(您可能需要右键单击并查看图像):
alt text http://img257.imageshack.us/img257/1124/40727627.png
答案 0 :(得分:4)
Visual Studio将显示停止的地方的调用堆栈。
如果是未处理的异常,它将停止抛出该异常的位置。即你的“抛出”声明。但是,如果您的代码处理异常,那么Visual Studio会假设您知道您正在做什么,并忽略该异常。它只会在Main()中重新抛出时捕获异常,因为你没有在程序中处理它。
如果您想在visual studio中捕获原始异常,您有两种选择:
不要在代码中捕获异常。默认情况下,Visual Studio仅停留在未处理的例外。这当然意味着你的程序不会在运行时处理异常,所以这不是一个非常有用的方法!
使用您的代码捕获并重新抛出异常(正如您所做的那样),但将Visual Studio配置为在首次抛出内部异常时停止。转到Debug> Exceptions并勾选“公共语言运行时”例外框(停止任何异常)或浏览子树以启用异常捕获特定异常(提示:如果您知道异常名称,请单击Find ...按钮并输入名称的一部分,例如“FileNotFound”,以快速找到异常)。这将使VS停止在内部异常,并且如果您在检查异常详细信息后选择继续执行,则仅转到catch {}语句。
答案 1 :(得分:3)
您可以抛出一个新异常,将异常e作为内部异常。然后阅读内部异常的堆栈跟踪。
答案 2 :(得分:3)
事实证明,如果您知道要搜索的正确术语,那么 就是我试图解决的问题的答案。在MSIL中,它被称为异常过滤和it is available in VS2003。
在Visual Basic.NET中,有一个名为“catch-when”的构造,它只在给定谓词通过时执行catch。 This MSDN blog有一个很好的例子,说明了VB.NET中的catch-when如何与C#的引人注目的结果(比如我的)一样。
最后,MSDN有一个名为Exception Filter Inject的工具,可用于“为没有异常过滤器支持的语言(例如C#)提供异常过滤器支持” - 问题是它在现有的上运行如果你最终使用它,它会在构建过程中引入一个尴尬的阶段。
在我找到Exception Filter Inject之前,我最终实现了一个带有“功能”委托和“catch”委托的短函数,并且如果允许异常被冒泡则调用函数,否则称为函数一个try-catch,在捕获的异常上调用catch委托。
我想要做什么,以某种方式引导我找到异常过滤,是为了能够设置在运行时捕获的异常类型 - 如果异常应该冒出来,我我会尝试捕获一个永远不会被调用的子类Exception,否则我会抓住一个基本的Exception。我真的不确定在.NET 1.1中是否可以实现这一点,因为这基本上需要一个泛型 - 但它可能已经有了反射,我在研究中从来没有这么远。
答案 3 :(得分:1)
如果我理解你的消息,那么在错误的堆栈跟踪和正确的错误的当前调用堆栈之间(这可能不是由你,而是由其他人阅读你的消息)你想要的是什么)
但是,一旦进入异常处理例程,Foo例程就完成了,所以我看不出它是如何成为当前调用堆栈的一部分。
除了启用'break at first exception'之外,我无法看到它是如何工作的,并且我不知道VS2003或VS2005中的任何内容会对此有所帮助。 (也许是VS2010中新的调试/重放功能)
答案 4 :(得分:1)
您所描述的是“调用堆栈”窗口的预期行为。当Visual Studio由于UnhandledException而在throw
行中断时,将调用堆栈显示在throw
行开始是正确的。
归结为Visual Studio的调用堆栈窗口不知道您的异常中包含的堆栈跟踪。