问题
I've confirmed this same behavior in VS2005, so I was wrong to call it a .NET (1.1) bug.
I'm leaving the original question below, but my revised question is this: how do I get Visual Studio to give me the stack trace of the exception I've caught and re-thrown in the Call Stack window, rather than only displaying the call stack from the point of the throw
statement?
The situation is that I am deciding at runtime whether the global exception handler is on or off -- if it's off, I want VS to catch the exception so I can step through the call stack to figure out what went wrong.
Previously, the global exception handler was either compiled into the program or not. But the situation has changed, and now we need to decide at runtime -- it's looking like I might need to go back to the macro way of doing it, but without macros:
if (allow_bubble_up)
{
Foo();
}
else
{
try
{
Foo();
}
catch (Exception e)
{
GlobalExceptionHandler(e);
}
}
But that approach feels extremely against DRY, to me.
Apparently there is a bug in .NET 1.1 where if you have an empty throw
statement to re-throw a caught exception, the stack trace is started from where that throw
occurred, instead of the stack trace of the whole exception being re-thrown -- at least, I've seen it called a bug on a couple blogs, but I haven't been able to get much more information on it.
To be a little more specific, the StackTrace
property of $exception
in QuickWatch shows the correct data, but the Call Stack window in VS only shows the call stack to the level of the throw statement.
In this sample code, I can only see a 1-level-deep stack trace of Main
, even though I should see a stack trace of a couple calls to 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 shows how to work around re-thrown stack traces of some sort for .NET 2.0+, and at the bottom he says to check Chris Taylor's blog for how to do it in .NET 1.1. I had to search a bit to find it on archive.org. I think I implemented it correctly, but I still got a stack trace just at main -- his explanation wasn't terribly clear, and I'd prefer not to mess with the code base (wrap existing set of functionality in another method) any more than necessary.
I can see the correct stack trace in the properties of the caught and re-raised exception, but the navigable stack trace that VS shows is useless since it only tracks from the throw
statement. If I never catch and re-throw the exception, I do get a full and proper stack trace.
How do I get the right stack trace displayed in VS? I'm hoping there's some sort of simple workaround, and that I've just been searching the wrong terms.
And unfortunately, it has to be VS2003+C# for this.
If it wasn't otherwise clear, here's a screenshot (you'll probably need to right-click and view image):
alt text http://img257.imageshack.us/img257/1124/40727627.png
回答1:
Visual Studio will show the call stack of the place where it stops.
In the case of an unhandled exception, it will stop where that exception is thrown. i.e. your "throw" statement. But if your code handles the exception, then Visual Studio assumes you know what you're doing, and ignores the exception. It will only catch the exception when it is re-thrown in Main() because you aren't handling it in your program.
If you want to catch the original exception in visual studio, you have two options:
don't catch the exception in your code. Visual Studio by default will only stop at unhandled exceptions. This of course means your program won't handle exceptions at runtime, so isn't a very useful approach!
use your code to catch and re-throw the exception (as you are doing), but configure Visual Studio to stop when the inner exception is first thrown. Go to Debug>Exceptions and tick the "common language runtime" exceptions box (to stop for any exception) or browse the subtree to enable exception catching for specific exceptions (hint: if you know the exception name, hit the Find... button and enter part of the name, e.g. "FileNotFound", to quickly find the exception). This will make VS stop at the inner exception, and only move on to your catch{} statement if you choose to continue execution after examining the exception details.
回答2:
You could throw a new exception, placing the exception e as an inner exception. Then read the stacktrace of the inner exception.
回答3:
It turns out that if you know the right terms to search, there is an answer to the problem I was trying to solve. In MSIL, it's called exception filtering, and it is available in VS2003.
In Visual Basic.NET, there is a construct called "catch-when" that will only execute a catch when a given predicate passes. This MSDN blog has a great example of how catch-when works in VB.NET vs. the results (like mine) of C#'s catch-throw.
Finally, MSDN has a tool called Exception Filter Inject that can be used to "provide exception filter support for languages (such as C#) which do not have exception filter support" - the catch is that it runs on an existing assembly, so it does introduce an awkward stage in the build process if you end up using it.
Before I found Exception Filter Inject, I ended up implementing a short function that took a "functional" delegate and a "catch" delegate, and would just call the functional if exceptions were allowed to bubble up, otherwise called the functional in a try-catch, calling the catch delegate on a caught exception.
What I wanted to do, that somehow lead me to find exception filtering, was to be able to set the type of the exception to catch at runtime - if exceptions were supposed to bubble up, I would have tried catching a subclassed Exception that would never be invoked, otherwise I would have just caught a basic Exception. I'm really not sure if that would have been possible in .NET 1.1 or not, since that basically would require a generic -- but it may have been possible with Reflection, I just never got that far in my research.
回答4:
If I understand your message, there is confusion (maybe not by you, but by others reading your message) between the stack trace of the error which is correct and the current call stack at a particular point in time (which is not what you want)
However, once you get into your exception handling routine, the Foo routine has finished and so I can't see how it could be part of your current call stack.
Apart from enabling 'break at first exception' , I can't see how this would work and am not aware of anything in VS2003 or VS2005 which will help this. (Maybe the new debugging/replay features in VS2010)
回答5:
What you're describing is the expected behavior of the Call Stack window. When Visual Studio breaks at the throw
line due to an UnhandledException, it is correct to have the call stack show starting at the throw
line.
It boils down to Visual Studio's Call Stack Window isn't aware of the stack trace contained in your exception.
来源:https://stackoverflow.com/questions/1364082/showing-stack-trace-of-exception-that-is-re-thrown-rather-than-stack-trace-from