Skip to content

Instantly share code, notes, and snippets.

@voroninp
Last active May 5, 2024 14:22
Show Gist options
  • Save voroninp/704c1cb556bd03697ef0c80524de1b24 to your computer and use it in GitHub Desktop.
Save voroninp/704c1cb556bd03697ef0c80524de1b24 to your computer and use it in GitHub Desktop.
Exception tracker
/// <summary>
/// Tracks exception which was thrown after instantiation of the tracker.
/// </summary>
/// <remarks>
/// It can be used to get the exception in finally block of try-catch-finally.
/// </remarks>
/// <example>
/// <code>
/// var tracker = new ExceptionTracker();
/// try
/// {
/// throw new InvalidOperationException();
/// }
/// finally
/// {
/// Console.WriteLine(tracker.BubblingUpException);
/// }
/// </code>
/// </example>
public readonly struct ExceptionTracker
{
[ThreadStatic]
private static WeakReference _currentException;
static ExceptionTracker()
{
AppDomain.CurrentDomain.FirstChanceException += (s, e) =>
{
_currentException = new WeakReference(e.Exception, trackResurrection: false);
};
}
private readonly IntPtr _exceptionAddress;
public ExceptionTracker()
{
_exceptionAddress = Marshal.GetExceptionPointers();
}
/// <summary>
/// Indicates exception was thrown after the tracker was instantiated.
/// If there's no exception or it was already propagating trough the stack,
/// this property will return <c>false</c>.
/// </summary>
private readonly bool IsExceptionBubblingUp
{
get
{
var curentExceptionAddr = Marshal.GetExceptionPointers();
var isExceptionBubblingUp = _exceptionAddress != curentExceptionAddr && curentExceptionAddr != IntPtr.Zero;
return isExceptionBubblingUp;
}
}
public readonly Exception? BubblingUpException
=>
IsExceptionBubblingUp && _currentException.Target is Exception ex
? ex
: null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment