10

Is there a way, how to get currently thrown exception (if exists)?

I would like reduce amount of code and apply some reuse for task looks like:

Exception thrownException = null;
try {
    // some code with 3rd party classes, which can throw unexpected exceptions
}
catch( Exception exc ) {
    thrownException = exc;
    LogException( exc );
}
finally {
    if ( null == thrownException ) {
        // some code
    }
    else {
        // some code
    }
}

and replace it with this code:

using( ExceptionHelper.LogException() ) {
    // some code with 3rd party classes, which can throw unexpected exceptions
}
using( new ExceptionHelper { ExceptionAction = ()=> /*some cleaning code*/ } ) {
    // some code with 3rd party classes, which can throw unexpected exceptions
}

public class ExceptiohHelper : IDisposable {
    public static ExceptionHelper LogException() {
        return new ExceptionHelper();
    }

    public Action SuccessfulAction {get; set;}
    public Action ExceptionAction {get; set;}

    public void Dispose() {
        Action action;
        Exception thrownException = TheMethodIDontKnow();
        if ( null != thrownException ) {
            LogException( thrownException );
            action = this.ExceptionAction;
        }
        else {
            action = this.SuccessfulAction;
        }

        if ( null != action ) {
            action();
        }
    }
}

Is this scenario posible?

Thanks

TcKs
  • 25,849
  • 11
  • 66
  • 104

4 Answers4

10

The idea is that you handle exceptions in the catch block...

That said, Exception is a reference type, so you can always declare an Exception variable outside the try scope...

Exception dontDoThis;
try
{
    foo.DoSomething();
}
catch(Exception e)
{
    dontDoThis = e;
}
finally
{
    // use dontDoThis...
}
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 3
    Yes, I know the way try-catch-finally. I use it now, as I said in the question - please read it one more time. But I want to reduce the writing code, and extend the reuse ability. This is not the answer in any way :( – TcKs Jan 14 '10 at 09:10
5

What do you think about the following. Instead of looking at the problem as "How to get the last exception?", what if you change it to, "How do I run some piece of code with some more control?"

For example: Instead of an ExceptionHelper you could have an ActionRunner.

public class ActionRunner
{
    public Action AttemptAction { get; set; }
    public Action SuccessfulAction { get; set; }
    public Action ExceptionAction { get; set; }

    public void RunAction()
    {
        try
        {
            AttemptAction();
            SuccessfulAction();
        }
        catch (Exception ex)
        {
            LogException(ex);
            ExceptionAction();
        }
    }

    private void LogException(Exception thrownException) { /* log here... */ }
}

It would at least give you some reuse of the SuccessfulAction and ExceptionAction assuming only the AttemptAction varies between calls.

var actionRunner = new ActionRunner
{
    AttemptAction = () =>
    {
        Console.WriteLine("Going to throw...");
        throw new Exception("Just throwing");
    },
    ExceptionAction = () => Console.WriteLine("ExceptionAction"),
    SuccessfulAction = () => Console.WriteLine("SuccessfulAction"),
};
actionRunner.RunAction();

actionRunner.AttemptAction = () => Console.WriteLine("Running some other code...");
actionRunner.RunAction();
Kim Major
  • 3,681
  • 1
  • 22
  • 20
3

If you are looking to catch unexpected exceptions you should be handling the UnhandledException. You should only catch exceptions at lower levels that you intend handle (not just to log), otherwise you should let them bubble up and be caught at a higher level, or as I mentioned before in the UnhandledException method.

James
  • 80,725
  • 18
  • 167
  • 237
  • The UnhandlerException is a global event, and I lost every context informations - it is useless for concrete exception-handling in different code. However, this is better answr than @Mark Seemann. Maybe there can be some way, how to dynamicelly attach/dettach the UnhandledException. I'll try it. – TcKs Jan 14 '10 at 09:14
  • The UnhandledException looks good, but if the exception is handled somewhere in caller code, the event is not raised (it makes sense - then is exception handled). But thanks, maybe I can found some "HandledException" event. – TcKs Jan 14 '10 at 09:33
  • If you still want your Exception to get handled in the UnhandledException but you want to deal with it internally, just handle it and then rethrow it. – James Jan 14 '10 at 10:29
  • I want skip try-catch-finally blocks, because there is no efective way, how-to reuse it in large projects and manage refactoring is very time consuming. The UnhandledException is raised only, if no catch-block is in above in stack trace. So if you have "void Main() { try { new Form1().ShowDialog(); } catch { ... }", the UnhandledException event is never fired. – TcKs Jan 14 '10 at 14:16
  • @Tcks...not true. The UnhandledException event is raised if you do not *handle* the raised exception. Unless of course you are catching Exception (the base class) to which I would say is bad design. – James Jan 14 '10 at 14:21
1

It's a very old question, but these days there are some possibilities.

This github issue contains an example of how this can be achieved:

async Task Main()
{
    using var activity1 = new Activity();
    try
    {
        using var activity2 = new Activity();
        await ThrowAsync();
    }
    catch
    {       
    }
}

public Task ThrowAsync() => Task.Run(() => throw new OperationCanceledException());

public sealed class Activity : IDisposable
{
    [ThreadStatic]
    private static GCHandle _currentExceptionHandle;
    
    static Activity()
    {
        AppDomain.CurrentDomain.FirstChanceException += (s,e) =>
        {
            if (_currentExceptionHandle.IsAllocated)
            {
                _currentExceptionHandle.Free();
            }
            _currentExceptionHandle = GCHandle.Alloc(e.Exception, GCHandleType.Weak);
        };
    }
    
    private readonly IntPtr _exceptionAddress;

    public Activity()
    {
        _exceptionAddress = Marshal.GetExceptionPointers();
    }

    public void Dispose()
    {
        var curentExceptionAddr = Marshal.GetExceptionPointers();
        var isExceptionBubblingUp = _exceptionAddress != curentExceptionAddr && curentExceptionAddr != IntPtr.Zero;
        if (isExceptionBubblingUp && _currentExceptionHandle.Target is Exception currentException)
        {
            Console.WriteLine($"Thrown exception {currentException}");
        }
        else if (_currentExceptionHandle.IsAllocated)
        {
            _currentExceptionHandle.Free();
        }
    }
}
Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137