Re-throwing Exceptions in C# with InternalPreserveStackTrace
It is often the case that you need to re-throw an exception after handling it. The common problem with re-throwing exceptions is that you often lose stack trace information that can be extraordinarily useful for debugging purposes. I'll explain a technique that can be used to force the entire stack to be propagated when an exception is re-thrown.
The solution is to perform a reflective invocation of the InternalPreserveStackTrace method, which is an internal method belonging to System.Exception. Usually, this can be accomplished in a single line of code using reflection as follows:
{
// DOES SOMETHING
}
catch (Exception ex)
{
typeof(System.Exception).GetMethod("InternalPreserveStackTrace",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.Invoke(ex, null);
throw;
}
The InternalPreserveStackTrace method simply grabs the StackTrace from the point of the exception to the location where InternalPreserveStackTrace method was invoked. It stores this trace information in a temporary field for usage in subsequent requests to StackTrace.
A little deeper analysis using Reflector shows exactly how this method works.
{
string stackTrace = this.StackTrace;
if ((stackTrace != null) && (stackTrace.Length > 0))
{
this._remoteStackTraceString = stackTrace + Environment.NewLine;
}
this._stackTrace = null;
this._stackTraceString = null;
}
public virtual string StackTrace
{
get
{
if (this._stackTraceString != null)
{
return (this._remoteStackTraceString + this._stackTraceString);
}
if (this._stackTrace == null)
{
return this._remoteStackTraceString;
}
string stackTrace = Environment.GetStackTrace(this, true);
return (this._remoteStackTraceString + stackTrace);
}
}
If you use reflection to analyze what this code does during execution you will see that it follows this process:
When the Exception enters the initial catch block it contains the following information: _stackTrace field contains an sbyte array, _stackTraceString is null, and _remoteStackTraceString is null.
Upon the invocation of the InternalPreserveStackTrace method, the StackTrace property's getter is invoked in the first line of InternalPreserveStackTrace.
When get_StackTrace is called neither of the the first two conditional statements execute because _stackTraceString is null and _stackTrace is not null. The Environment.GetStrackTrace method is invoked and the stack from the source of the exception to the first catch block is returned and stored in the stackTrace variable.
InternalPreserveStackTrace then checks if the stackTrace variable is not null and has a length. Because this variable now contains the stack trace from the source of the exception, the condition is true and it stores the stackTrace information in the _remoteStackTraceString field of the current Exception. Finally, it sets the _stackTrace and _stackTraceString fields to null.
If the StackTrace property is now accessed it will only return the contents of the _remoteStackTraceString field.
At this point in our application the Exception is re-thrown using either
or
When the exception is subsequently caught again, the _stackTrace field will once again be set to an sbyte array since the stack has changed. This time, the _remoteStackTraceString field will contain our original stack trace from the source of the exception to the first catch block. This stack trace information is then combined with the stack trace from the re-throw to give a complete picture of the stack at the time the exception occurred.
CAVEAT EMPTOR
Because this technique uses reflection to access private fields in the Exception class by using the NonPublic BindingFlag, it may not work if the system does not have FullTrust enabled. I have not investigated this yet... so beware in hosted environments that do not execute with Full Trust.
Recent blog posts
- Canned VirtualPC Instances for IE 6, 7, 8 on XP/Vista
- Checking assembly dependencies for .NET
- Google's Public DNS
- Server Utility Functions for Non-Web Apps
- reCAPTCHA for ASP.NET MVC that uses ModelState
- Adding a container to ValidationSummary helper in ASP.NET MVC
- Generic XML Serialization Class
- Re-throwing Exceptions in C# with InternalPreserveStackTrace
- Solving xsd generation error: 'The element .... is missing'
- Enum DropDownList in ASP.NET MVC
Recent comments
9 weeks 2 days ago
12 weeks 3 days ago
15 weeks 4 days ago
21 weeks 2 days ago
31 weeks 1 day ago
1 year 15 weeks ago
1 year 32 weeks ago
1 year 33 weeks ago
1 year 39 weeks ago
2 years 16 weeks ago