40

I'm working with Windows Forms application and hava a manager class that uses System.Timers.Timer to periodly check data from database.

How do I get the Exception that occurs in timer Elapsed eventhandler delivered into main application? If I'm using the code below, the exception get's "swallowed", and main application never gets it (even if I have handlers for ThreadException and UnHandledException).

// Main Form
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

// Manager class
private System.Timers.Timer _timer;

    void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            doSomeDatabaseActions();
        }
        catch (Exception ex)
        {
            throw new ApplicationException("How do I get this error back into main thread...", ex);
        }
    }
Clack
  • 945
  • 5
  • 11
  • 18

4 Answers4

38

If you don't have access to the main thread, you can throw the exception on another, non-timer thread:

catch (Exception exception)
{
    ThreadPool.QueueUserWorkItem(
        _ => { throw new Exception("Exception on timer.", exception); });
}
svick
  • 236,525
  • 50
  • 385
  • 514
  • Nice. This preserves the stack trace and pumps the exception to AppDomain.UnhandledException. Thank you! – Nathan Oct 10 '11 at 20:14
  • 1
    I have an infinite loop using this approach. The exception is thrown and handled by the `AppDomain.UnhandledException` always. How can I avoid this? – anmarti Sep 13 '13 at 11:36
  • This requires wrapping whole function inside try catch block right ? – Furkan Gözükara Aug 21 '14 at 13:03
  • @MonsterMMORPG Yeah, just like in the question. – svick Aug 21 '14 at 14:47
  • This worked for me in a slightly different context, with exceptions thrown out of worker threads inside a Windows Service. Since the threads are spun up by a timer Elapsed event, this same thing was happening. – Mike K Mar 31 '15 at 16:25
  • I don't understand. How can I catch the exception now in my main thread? It does not get caught by Application.ThreadExceptio. It is caught by AppDomain.CurrentDomain.UnhandledException, but this terminates the appliction. – user74696c Oct 06 '20 at 06:46
27

Since System.Timers.Timer swallows any exception thrown in the event handler, you will need to marshal the exception to another thread (probably the UI thread). You could do this via Control.Invoke, or by storing error information in a member variable and having the UI thread check this error information after the operation is complete. If non-null, the UI could then throw.

From MSDN:

In the .NET Framework version 2.0 and earlier, the Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event. This behavior is subject to change in future releases of the .NET Framework.

Just checked in .NET 4.0, and this behavior has not yet changed.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • I think I get it, I passed the exception to a function on my main thread and then that function used this.Invoke() on the function before re throwing the exception. Am I understanding this correctly? – hrh Apr 25 '12 at 16:41
  • 2
    Are there any new method to catch unexpected errors at .net 4.5 ? It still swallows and are'nt there anyway to log errors without wrapping whole _timer_Elapsed inside try catch ? – Furkan Gözükara Aug 21 '14 at 13:02
4

You can assign the exception to a local variable and check if an exception has been thrown:

// Main Form
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

// Manager class
private System.Timers.Timer _timer;

    private exception = null;

    void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        //reset the exception in case object is reused.
        this.exception = null;
        try
        {
            doSomeDatabaseActions();
        }
        catch (Exception ex)
        {
            this.exception = ex;
        }
    }

    /**
    * Checks whether the exception object is set.
    */
    public bool hasExceptionOccured(){
        return (this.exception != null);
    }

    //The main application will call this to get the exception.
    public Exception getException(){
        return this.exception;
    }
Koekiebox
  • 5,793
  • 14
  • 53
  • 88
2

I guess that you want to handle the exception on the main form, this solution is not complete but shows how to do it with an "Action"

using System;
using System.Timers;


public class MainForm
{
    public MainForm()
    {
        var tm = new TestManager(exception =>
        {
            //do somthing with exception
            //note you are still on the timer event thread
        });

    }
}

public class TestManager
{
    private readonly Action<Exception> _onException;

    public TestManager(System.Action<System.Exception> onException )
    {
        _onException = onException;

    }


    private System.Timers.Timer _timer;
    void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            doSomeDatabaseActions();
        }
        catch (Exception ex)
        {
            //throw new ApplicationException("How do I get this error back into main thread...", ex);
            _onException(ex);
        }
    }

}
Nickolai Nielsen
  • 932
  • 6
  • 19