This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Show line number in exception handling
Can someone please tell me how to get the line number of the code where the error occurred and display it to the console?
Other information like the file name or method name would be very handy.
If you want the file and line numbers, you do not need to parse the StackTrace string. You can use System.Diagnostics.StackTrace to create a stack trace from an exception, with this you can enumerate the stack frames and get the filename, line number and column that the exception was raised. Here is a quick and dirty example of how to do this. No error checking included. For this to work a PDB needs to exist with the debug symbols, this is created by default with debug build.
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
TestFunction();
}
catch (Exception ex)
{
StackTrace st = new StackTrace(ex, true);
StackFrame[] frames = st.GetFrames();
// Iterate over the frames extracting the information you need
foreach (StackFrame frame in frames)
{
Console.WriteLine("{0}:{1}({2},{3})", frame.GetFileName(), frame.GetMethod().Name, frame.GetFileLineNumber(), frame.GetFileColumnNumber());
}
}
Console.ReadKey();
}
static void TestFunction()
{
throw new InvalidOperationException();
}
}
}
The output from the above code looks like this
D:\Source\NGTests\ConsoleApplication1\Program.cs:TestFunction(30,7)
D:\Source\NGTests\ConsoleApplication1\Program.cs:Main(11,9)
You can print the entire stack trace by using a try/catch around the code that can throw and then using Console.WriteLine to show the exception object:
try
{
new Program().Run();
}
catch (Exception exception) // Prefer to catch a more specific execption.
{
Console.WriteLine(exception);
}
Output:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Program.Run() in C:\Console Application1\Program.cs:line 37
at Program.Main(String[] args) in C:\Console Application1\Program.cs:line 45
The first line shows the type of the exception and the message. The second line shows the file, function and line number where the exception was thrown. You can also see the locations of other calls on the call stack in the following lines.
You can also get file and line numbers for uncaught exceptions. You can do this by adding a handler for the AppDomain.UncaughtException event on the current AppDomain:
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
new Program().Run();
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine(e.ExceptionObject);
}
This shows a similar output to above.
Console.WriteLine(exception.StackTrace);
Make sure your application is in Debug mode or include the debug symbols (the .mdb file) in order for line numbers to appear.
You can get the stack trace by accessing Exception.StackTrace which is a string so you can print it to the console by using the Write or WriteLine methods.
You can find it in the stack trace (Exception.StackTrace property), on the last line, but only when your code has been compiled with debugging information included. Otherwise the line number will be unknown.
Related
As i already now, to keep the original stackTrace we should be avoiding using throw caught exception, instead, we should just use throw clause that in fact rethrow the exception.
The thing is that a few days age in an interview i was given a code to predict the stacktrace of final result:
class Program
{
static void Main(string[] args)
{
try
{
ReThrowException(); // line 17
}
catch (Exception x)
{
Console.WriteLine("Exception:");
Console.WriteLine(x.StackTrace); //line 22
}
}
private static void ReThrowException()
{
try
{
DivByZero();
}
catch
{
throw; //line 34
}
}
private static void DivByZero()
{
int x = 0;
int y = 1 / x; //line 41
}
}
Exception occurs in line 41 and in line 34 it just rethrow exception by keeping the original stackTrace.
My answer was:
it just should be pointing to the line 41where exception actually
occurred for the first time and nowhere stackTrace is overwriting in
the code.
But the true answer was as follows:
Exception:
at TestConsole.Program.DivByZero() in C:...\Projects\TestConsole\TestConsole\Program.cs:line 41
at TestConsole.Program.ReThrowException() in C:...\Projects\TestConsole\TestConsole\Program.cs:line 34
at TestConsole.Program.Main(String[] args) in C:...\Projects\TestConsole\TestConsole\Program.cs:line 17
Now two questions come to my mind:
1.Why stackTrace is adding sources for lines 17,34?
2.What should i do in the code to just stackTrace point to the line 41 and remove other sources?
1.Why stackTrace is adding sources for lines 17,34?
It's not other sources, it's stack trace that shows the trace that leads to exception.
As the name StackTrace implies, it is not a single point, likewise it is a path.
2.What should I do in the code to just stackTrace point to the line 41 and remove other sources?
You cant, because your question comes from an misunderstanding regarding existence of stackTrace.
"What should i do in the code to just stackTrace point to the line 41 and remove other sources?"
Don't catch it on line 34?
You should only catch an exception you know how to handle. If you ever catch() or catch(Exception) then you are probably wrong (unless it's for logging and you rethrow).
Remember, exceptions are 'expensive'. You should try and avoid them. Dividing by zero is each to stop, and if it's 'normal' for your code to have a zero divisor, then that's not an exception and should be trapped with an if.
See edits below for reproducing the behavior that I describe in this problem.
The following program will never end, because the yield return construct in C# calls the GetStrings() method indefinitely when an exception is thrown.
class Program
{
static void Main(string[] args)
{
// I expect the Exception to be thrown here, but it's not
foreach (var str in GetStrings())
{
Console.WriteLine(str);
}
}
private static IEnumerable<string> GetStrings()
{
// REPEATEDLY throws this exception
throw new Exception();
yield break;
}
}
For this trivial example, I could obviously use return Enumerable.Empty<string>(); instead, and the problem goes away. However in a more interesting example, I'd expect the exception to be thrown once, then have the method stop being called and throw the exception in the method that's "consuming" the IEnumerable.
Is there a way to produce this behavior?
EDIT: ok, the problem is different than I first thought. The program above does NOT end, and the foreach loop behaves like an infinite loop. The program below DOES end, and the exception is displayed on the console.
class Program
{
static void Main(string[] args)
{
try
{
foreach (var str in GetStrings())
{
Console.WriteLine(str);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static IEnumerable<string> GetStrings()
{
throw new Exception();
yield break;
}
}
Why does the try ... catch block make a difference in this case? This seems very strange to me. Thanks to #AndrewKilburn for his answer already for pointing me to this.
EDIT #2:
From a Command Prompt, the program executes the same in both cases. In Visual Studio Enterprise 2015, Update 2, whether I compile in Debug or Release, the behavior above is what I am seeing. With the try ... catch, the program ends with an exception, and without it Visual Studio never closes the program.
EDIT #3: Fixed
For me, the issue was resolved by the answer by #MartinBrown. When I uncheck Visual Studio's option under Debug > Options > Debugging > General > "Unwind the call stack on unhandled exceptions" this problem goes away. When I check the box again, then the problem comes back.
The behaviour being seen here is not a fault in the code; rather it is a side effect of the Visual Studio debugger. This can be resolved by turning off stack unwinding in Visual Studio. Try going into Visual Studio options Debugging/General and unchecking "Unwind the call stack on unhandled exceptions". Then run the code again.
What happens is that when your code hits a completely unhandled exception Visual Studio is unwinding the call stack to just before the line in your code that caused the exception. It does this so that you can edit the code and continue execution with the edited code.
The issue seen here looks like an infinite loop because when you re-start execution in the debugger the next line to run is the one that just caused an exception. Outside the debugger the call stack would be completely unwound on an unhandled exception and thus would not cause the same loop that you get in the debugger.
This stack unwinding feature can be turned off in the settings, it is enabled by default. Turning it off however will stop you being able to edit code and continue without first unwinding the stack yourself. This however is quite easy to do either from the call stack window or by simply selecting 'Enable Editing' from the Exception Assistant.
The following program will never end
That's false. The program is going to end quite quickly.
because the yield return construct in C# calls the GetStrings() method indefinitely when an exception is thrown.
This is false. It doesn't do this at all.
I'd expect the exception to be thrown once, then have the method stop being called and throw the exception in the method that's "consuming" the IEnumerable.
That's exactly what does happen.
Is there a way to produce this behavior?
Use the code you already provided.
class Program {
static void Main(string[] args) {
try {
foreach (var item in GetStrings()) {
Console.WriteLine();
}
}
catch (Exception ex) {
}
}
private static IEnumerable<string> GetStrings() {
// REPEATEDLY throws this exception
throw new Exception();
yield break;
}
}
Putting it in a try catch causes it to break out and do whatever you want
class Program
{
public static int EnumerableCount;
static void Main(string[] args)
{
EnumerableCount = 0;
try
{
foreach (var str in GetStrings())
{
Console.WriteLine(str);
Console.Read();
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.Read();
}
}
private static IEnumerable<string> GetStrings()
{
EnumerableCount++;
var errorMessage = string.Format("EnumerableCount: {0}", EnumerableCount);
throw new Exception(errorMessage);
yield break;
}
}
has the following output:
System.Exception: EnumerableCount: 1
at {insert stack trace here}
The execution flow goes into the GetStrings() method the for the first iteration, the exception is thrown and caught in the Main() method. After hitting enter, the program exits.
Removing the try catch in the Main() method causes the exception to go unhandled. The output is then:
Unhandled Exception: System.Exception: EnumerableCount: 1
at {insert stack trace here}
and the program crashes.
I rethrow an exception with "throw;", but the stacktrace is incorrect:
static void Main(string[] args) {
try {
try {
throw new Exception("Test"); //Line 12
}
catch (Exception ex) {
throw; //Line 15
}
}
catch (Exception ex) {
System.Diagnostics.Debug.Write(ex.ToString());
}
Console.ReadKey();
}
The right stacktrace should be:
System.Exception: Test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 12
But I get:
System.Exception: Test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 15
But line 15 is the position of the "throw;". I have tested this with .NET 3.5.
Throwing twice in the same method is probably a special case - I've not been able to create a stack trace where different lines in the same method follow each other. As the word says, a "stack trace" shows you the stack frames that an exception traversed. And there is only one stack frame per method call!
If you throw from another method, throw; will not remove the entry for Foo(), as expected:
static void Main(string[] args)
{
try
{
Rethrower();
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
Console.ReadKey();
}
static void Rethrower()
{
try
{
Foo();
}
catch (Exception ex)
{
throw;
}
}
static void Foo()
{
throw new Exception("Test");
}
If you modify Rethrower() and replace throw; by throw ex;, the Foo() entry in the stack trace disappears. Again, that's the expected behavior.
It's something that can be considered as expected.
Modifying stack trace is usual case if you specify throw ex;, FxCop will than notify you that stack is modified. In case you make throw;, no warning is generated, but still, the trace will be modified.
So unfortunately for now it's the best not to catch the ex or throw it as an inner one.
I think it should be considered as a Windows impact or smth like that - edited.
Jeff Richter describes this situation in more detail in his "CLR via C#":
The following code throws the same
exception object that it caught and
causes the CLR to reset its starting
point for the exception:
private void SomeMethod() {
try { ... }
catch (Exception e) {
...
throw e; // CLR thinks this is where exception originated.
// FxCop reports this as an error
}
}
In contrast, if you re-throw an
exception object by using the throw
keyword by itself, the CLR doesn’t
reset the stack’s starting point. The
following code re-throws the same
exception object that it caught,
causing the CLR to not reset its
starting point for the exception:
private void SomeMethod() {
try { ... }
catch (Exception e) {
...
throw; // This has no effect on where the CLR thinks the exception
// originated. FxCop does NOT report this as an error
}
}
In fact, the only difference between
these two code fragments is what the
CLR thinks is the original location
where the exception was thrown.
Unfortunately, when you throw or
rethrow an exception, Windows does
reset the stack’s starting point. So
if the exception becomes unhandled,
the stack location that gets reported
to Windows Error Reporting is the
location of the last throw or
re-throw, even though the CLR knows
the stack location where the original
exception was thrown. This is
unfortunate because it makes debugging
applications that have failed in the
field much more difficult. Some
developers have found this so
intolerable that they have chosen a
different way to implement their code
to ensure that the stack trace truly
reflects the location where an
exception was originally thrown:
private void SomeMethod() {
Boolean trySucceeds = false;
try {
...
trySucceeds = true;
}
finally {
if (!trySucceeds) { /* catch code goes in here */ }
}
}
This is a well known limitation in the Windows version of the CLR. It uses Windows' built-in support for exception handling (SEH). Problem is, it is stack frame based and a method has only one stack frame. You can easily solve the problem by moving the inner try/catch block into another helper method, thus creating another stack frame. Another consequence of this limitation is that the JIT compiler won't inline any method that contains a try statement.
How can I preserve the REAL stacktrace?
You throw a new exception, and include the original exception as the inner exception.
but that's Ugly... Longer... Makes you choice the rigth exception to throw....
You are wrong about the ugly but right about the other two points. The rule of thumb is: don't catch unless you are going to do something with it, like wrap it, modify it, swallow it, or log it. If you decide to catch and then throw again, make sure you are doing something with it, otherwise just let it bubble up.
You may also be tempted to put a catch simply so you can breakpoint within the catch, but the Visual Studio debugger has enough options to make that practice unnecessary, try using first chance exceptions or conditional breakpoints instead.
Edit/Replace
The behavior is actually different, but subtilely so. As for why the behavior if different, I'll need to defer to a CLR expert.
EDIT: AlexD's answer seems to indicate that this is by design.
Throwing the exception in the same method that catches it confuses the situation a little, so let's throw an exception from another method:
class Program
{
static void Main(string[] args)
{
try
{
Throw();
}
catch (Exception ex)
{
throw ex;
}
}
public static void Throw()
{
int a = 0;
int b = 10 / a;
}
}
If throw; is used, the callstack is (line numbers replaced with code):
at Throw():line (int b = 10 / a;)
at Main():line (throw;) // This has been modified
If throw ex; is used, the callstack is:
at Main():line (throw ex;)
If exception is not caught, the callstack is:
at Throw():line (int b = 10 / a;)
at Main():line (Throw())
Tested in .NET 4 / VS 2010
There is a duplicate question here.
As I understand it - throw; is compiled into 'rethrow' MSIL instruction and it modifies the last frame of the stack-trace.
I would expect it to keep the original stack-trace and add the line where it has been re-thrown, but apparently there can only be one stack frame per method call.
Conclusion: avoid using throw; and wrap your exception in a new one on re-throwing - it's not ugly, it's best practice.
You can preserve stack trace using
ExceptionDispatchInfo.Capture(ex);
Here is code sample:
static void CallAndThrow()
{
throw new ApplicationException("Test app ex", new Exception("Test inner ex"));
}
static void Main(string[] args)
{
try
{
try
{
try
{
CallAndThrow();
}
catch (Exception ex)
{
var dispatchException = ExceptionDispatchInfo.Capture(ex);
// rollback tran, etc
dispatchException.Throw();
}
}
catch (Exception ex)
{
var dispatchException = ExceptionDispatchInfo.Capture(ex);
// other rollbacks
dispatchException.Throw();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.InnerException.Message);
Console.WriteLine(ex.StackTrace);
}
Console.ReadLine();
}
The output will be something like:
Test app ex
Test inner ex
at TestApp.Program.CallAndThrow() in D:\Projects\TestApp\TestApp\Program.cs:line 19
at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 30
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 38
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 47
OK, there seems to be a bug in the .NET Framework, if you throw an exception, and rethrow it in the same method, the original line number is lost (it will be the last line of the method).
Fortunatelly, a clever guy named Fabrice MARGUERIE found a solution to this bug. Below is my version, which you can test in this .NET Fiddle.
private static void RethrowExceptionButPreserveStackTrace(Exception exception)
{
System.Reflection.MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
throw exception;
}
Now catch the exception as usually, but instead of throw; just call this method, and voila, the original line number will be preserved!
Not sure whether this is by design, but I think it has always been like that.
If the original throw new Exception is in a separate method, then the result for throw should have the original method name and line number and then the line number in main where the exception is re-thrown.
If you use throw ex, then the result will just be the line in main where the exception is rethrow.
In other words, throw ex loses all the stacktrace, whereas throw preserves the stack trace history (ie details of the lower level methods). But if your exception is generated by the same method as your rethrow, then you can lose some information.
NB. If you write a very simple and small test program, the Framework can sometimes optimise things and change a method to be inline code which means the results may differ from a 'real' program.
Do you want your right line number? Just use one try/catch per method. In systems, well... just in the UI layer, not in logic or data access, this is very annoying, because if you need database transactions, well, they shouldn't be in the UI layer, and you won't have the right line number, but if you don't need them, don't rethrow with nor without an exception in catch...
5 minutes sample code:
Menu File -> New Project, place three buttons, and call the following code in each one:
private void button1_Click(object sender, EventArgs e)
{
try
{
Class1.testWithoutTC();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
}
}
private void button2_Click(object sender, EventArgs e)
{
try
{
Class1.testWithTC1();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
}
}
private void button3_Click(object sender, EventArgs e)
{
try
{
Class1.testWithTC2();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
}
}
Now, create a new Class:
class Class1
{
public int a;
public static void testWithoutTC()
{
Class1 obj = null;
obj.a = 1;
}
public static void testWithTC1()
{
try
{
Class1 obj = null;
obj.a = 1;
}
catch
{
throw;
}
}
public static void testWithTC2()
{
try
{
Class1 obj = null;
obj.a = 1;
}
catch (Exception ex)
{
throw ex;
}
}
}
Run... the first button is beautiful!
I think this is less a case of stack trace changing and more to do with the way the line number for the stack trace is determined. Trying it out in Visual Studio 2010, the behaviour is similar to what you would expect from the MSDN documentation: "throw ex;" rebuilds the stack trace from the point of this statement, "throw;" leaves the stack trace as it as, except that where ever the exception is rethrown, the line number is the location of the rethrow and not the call the exception came through.
So with "throw;" the method call tree is left unaltered, but the line numbers may change.
I've come across this a few times, and it may be by design and just not documented fully. I can understand why they may have done this as the rethrow location is very useful to know, and if your methods are simple enough the original source would usually be obvious anyway.
As many other people have said, it usually best to not catch the exception unless you really have to, and/or you are going to deal with it at that point.
Interesting side note: Visual Studio 2010 won't even let me build the code as presented in the question as it picks up the divide by zero error at compile time.
That is because you catched the Exception from Line 12 and have rethrown it on Line 15, so the Stack Trace takes it as cash, that the Exception was thrown from there.
To better handle exceptions, you should simply use try...finally, and let the unhandled Exception bubble up.
I need a suggestion regarding Object reference issue. I got this message ExceptionMessage: Object reference not set to an instance of an object. (because some null object) I am printing the stack trace when an error occurs:
Exception systemException = lastError.GetBaseException();
By using ex.Message I am able to get the method where exception has occurred but I am not getting any information regarding the line (of that method) where exception occurred. I need to know at which line object reference has occurred and which object was null when this exception came.
You need the actual stacktrace:
systemException.StackTrace
If you want the line numbers, you will need to do something like this:
StackTrace st = new StackTrace(systemException, true);
StackFrame[] frames = st.GetFrames();
foreach(StackFrame frame in frames)
frame.GetFileLineNumber();
That line number is pretty much guaranteed to be in the stack trace. If you want to drill down on it, put a try-catch in the large block of code where it might be happening and drop a breakpoint on the catch block, which will allow you to drop down through the InnerExceptions inside of visual studio (if any exist).
Maybe this helps
class Program
{
static void Main(string[] args)
{
try
{
string a = null;
a.ToString();
}
catch (Exception ex)
{
var s = ex.StackTrace;
Console.WriteLine(ex.Source);
int st = s.LastIndexOf("line");
Console.WriteLine(s.Substring(st, s.Length - st));
}
}
}
Edited :
And yes I just learned, using StrackTrace in the answer posted by EkoostikMartin.
I think it is a better answer.
I rethrow an exception with "throw;", but the stacktrace is incorrect:
static void Main(string[] args) {
try {
try {
throw new Exception("Test"); //Line 12
}
catch (Exception ex) {
throw; //Line 15
}
}
catch (Exception ex) {
System.Diagnostics.Debug.Write(ex.ToString());
}
Console.ReadKey();
}
The right stacktrace should be:
System.Exception: Test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 12
But I get:
System.Exception: Test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 15
But line 15 is the position of the "throw;". I have tested this with .NET 3.5.
Throwing twice in the same method is probably a special case - I've not been able to create a stack trace where different lines in the same method follow each other. As the word says, a "stack trace" shows you the stack frames that an exception traversed. And there is only one stack frame per method call!
If you throw from another method, throw; will not remove the entry for Foo(), as expected:
static void Main(string[] args)
{
try
{
Rethrower();
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
Console.ReadKey();
}
static void Rethrower()
{
try
{
Foo();
}
catch (Exception ex)
{
throw;
}
}
static void Foo()
{
throw new Exception("Test");
}
If you modify Rethrower() and replace throw; by throw ex;, the Foo() entry in the stack trace disappears. Again, that's the expected behavior.
It's something that can be considered as expected.
Modifying stack trace is usual case if you specify throw ex;, FxCop will than notify you that stack is modified. In case you make throw;, no warning is generated, but still, the trace will be modified.
So unfortunately for now it's the best not to catch the ex or throw it as an inner one.
I think it should be considered as a Windows impact or smth like that - edited.
Jeff Richter describes this situation in more detail in his "CLR via C#":
The following code throws the same
exception object that it caught and
causes the CLR to reset its starting
point for the exception:
private void SomeMethod() {
try { ... }
catch (Exception e) {
...
throw e; // CLR thinks this is where exception originated.
// FxCop reports this as an error
}
}
In contrast, if you re-throw an
exception object by using the throw
keyword by itself, the CLR doesn’t
reset the stack’s starting point. The
following code re-throws the same
exception object that it caught,
causing the CLR to not reset its
starting point for the exception:
private void SomeMethod() {
try { ... }
catch (Exception e) {
...
throw; // This has no effect on where the CLR thinks the exception
// originated. FxCop does NOT report this as an error
}
}
In fact, the only difference between
these two code fragments is what the
CLR thinks is the original location
where the exception was thrown.
Unfortunately, when you throw or
rethrow an exception, Windows does
reset the stack’s starting point. So
if the exception becomes unhandled,
the stack location that gets reported
to Windows Error Reporting is the
location of the last throw or
re-throw, even though the CLR knows
the stack location where the original
exception was thrown. This is
unfortunate because it makes debugging
applications that have failed in the
field much more difficult. Some
developers have found this so
intolerable that they have chosen a
different way to implement their code
to ensure that the stack trace truly
reflects the location where an
exception was originally thrown:
private void SomeMethod() {
Boolean trySucceeds = false;
try {
...
trySucceeds = true;
}
finally {
if (!trySucceeds) { /* catch code goes in here */ }
}
}
This is a well known limitation in the Windows version of the CLR. It uses Windows' built-in support for exception handling (SEH). Problem is, it is stack frame based and a method has only one stack frame. You can easily solve the problem by moving the inner try/catch block into another helper method, thus creating another stack frame. Another consequence of this limitation is that the JIT compiler won't inline any method that contains a try statement.
How can I preserve the REAL stacktrace?
You throw a new exception, and include the original exception as the inner exception.
but that's Ugly... Longer... Makes you choice the rigth exception to throw....
You are wrong about the ugly but right about the other two points. The rule of thumb is: don't catch unless you are going to do something with it, like wrap it, modify it, swallow it, or log it. If you decide to catch and then throw again, make sure you are doing something with it, otherwise just let it bubble up.
You may also be tempted to put a catch simply so you can breakpoint within the catch, but the Visual Studio debugger has enough options to make that practice unnecessary, try using first chance exceptions or conditional breakpoints instead.
Edit/Replace
The behavior is actually different, but subtilely so. As for why the behavior if different, I'll need to defer to a CLR expert.
EDIT: AlexD's answer seems to indicate that this is by design.
Throwing the exception in the same method that catches it confuses the situation a little, so let's throw an exception from another method:
class Program
{
static void Main(string[] args)
{
try
{
Throw();
}
catch (Exception ex)
{
throw ex;
}
}
public static void Throw()
{
int a = 0;
int b = 10 / a;
}
}
If throw; is used, the callstack is (line numbers replaced with code):
at Throw():line (int b = 10 / a;)
at Main():line (throw;) // This has been modified
If throw ex; is used, the callstack is:
at Main():line (throw ex;)
If exception is not caught, the callstack is:
at Throw():line (int b = 10 / a;)
at Main():line (Throw())
Tested in .NET 4 / VS 2010
There is a duplicate question here.
As I understand it - throw; is compiled into 'rethrow' MSIL instruction and it modifies the last frame of the stack-trace.
I would expect it to keep the original stack-trace and add the line where it has been re-thrown, but apparently there can only be one stack frame per method call.
Conclusion: avoid using throw; and wrap your exception in a new one on re-throwing - it's not ugly, it's best practice.
You can preserve stack trace using
ExceptionDispatchInfo.Capture(ex);
Here is code sample:
static void CallAndThrow()
{
throw new ApplicationException("Test app ex", new Exception("Test inner ex"));
}
static void Main(string[] args)
{
try
{
try
{
try
{
CallAndThrow();
}
catch (Exception ex)
{
var dispatchException = ExceptionDispatchInfo.Capture(ex);
// rollback tran, etc
dispatchException.Throw();
}
}
catch (Exception ex)
{
var dispatchException = ExceptionDispatchInfo.Capture(ex);
// other rollbacks
dispatchException.Throw();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.InnerException.Message);
Console.WriteLine(ex.StackTrace);
}
Console.ReadLine();
}
The output will be something like:
Test app ex
Test inner ex
at TestApp.Program.CallAndThrow() in D:\Projects\TestApp\TestApp\Program.cs:line 19
at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 30
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 38
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 47
OK, there seems to be a bug in the .NET Framework, if you throw an exception, and rethrow it in the same method, the original line number is lost (it will be the last line of the method).
Fortunatelly, a clever guy named Fabrice MARGUERIE found a solution to this bug. Below is my version, which you can test in this .NET Fiddle.
private static void RethrowExceptionButPreserveStackTrace(Exception exception)
{
System.Reflection.MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
throw exception;
}
Now catch the exception as usually, but instead of throw; just call this method, and voila, the original line number will be preserved!
Not sure whether this is by design, but I think it has always been like that.
If the original throw new Exception is in a separate method, then the result for throw should have the original method name and line number and then the line number in main where the exception is re-thrown.
If you use throw ex, then the result will just be the line in main where the exception is rethrow.
In other words, throw ex loses all the stacktrace, whereas throw preserves the stack trace history (ie details of the lower level methods). But if your exception is generated by the same method as your rethrow, then you can lose some information.
NB. If you write a very simple and small test program, the Framework can sometimes optimise things and change a method to be inline code which means the results may differ from a 'real' program.
Do you want your right line number? Just use one try/catch per method. In systems, well... just in the UI layer, not in logic or data access, this is very annoying, because if you need database transactions, well, they shouldn't be in the UI layer, and you won't have the right line number, but if you don't need them, don't rethrow with nor without an exception in catch...
5 minutes sample code:
Menu File -> New Project, place three buttons, and call the following code in each one:
private void button1_Click(object sender, EventArgs e)
{
try
{
Class1.testWithoutTC();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
}
}
private void button2_Click(object sender, EventArgs e)
{
try
{
Class1.testWithTC1();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
}
}
private void button3_Click(object sender, EventArgs e)
{
try
{
Class1.testWithTC2();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
}
}
Now, create a new Class:
class Class1
{
public int a;
public static void testWithoutTC()
{
Class1 obj = null;
obj.a = 1;
}
public static void testWithTC1()
{
try
{
Class1 obj = null;
obj.a = 1;
}
catch
{
throw;
}
}
public static void testWithTC2()
{
try
{
Class1 obj = null;
obj.a = 1;
}
catch (Exception ex)
{
throw ex;
}
}
}
Run... the first button is beautiful!
I think this is less a case of stack trace changing and more to do with the way the line number for the stack trace is determined. Trying it out in Visual Studio 2010, the behaviour is similar to what you would expect from the MSDN documentation: "throw ex;" rebuilds the stack trace from the point of this statement, "throw;" leaves the stack trace as it as, except that where ever the exception is rethrown, the line number is the location of the rethrow and not the call the exception came through.
So with "throw;" the method call tree is left unaltered, but the line numbers may change.
I've come across this a few times, and it may be by design and just not documented fully. I can understand why they may have done this as the rethrow location is very useful to know, and if your methods are simple enough the original source would usually be obvious anyway.
As many other people have said, it usually best to not catch the exception unless you really have to, and/or you are going to deal with it at that point.
Interesting side note: Visual Studio 2010 won't even let me build the code as presented in the question as it picks up the divide by zero error at compile time.
That is because you catched the Exception from Line 12 and have rethrown it on Line 15, so the Stack Trace takes it as cash, that the Exception was thrown from there.
To better handle exceptions, you should simply use try...finally, and let the unhandled Exception bubble up.