LIU
LIU

Reputation: 315

WPF Dispatcher.BeginInvoke Throw Exception

I am using Threadpool.QueueUserWorkItem like following

public void TestMain()
    {
        try
        {
            ThreadPool.QueueUserWorkItem(x =>
                {
                    this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod()));
                }
            );
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

    }

    private void BackGroundMethod()
    {
        try
        {
            int a = 0;
            int b = 100;
            var error = b / a;
        }
        catch(Exception)
        {
            throw;
        }
    }

By this way, TestMain() can not catch exception. Program will shut down...

How can i catch this error?

Thx.

Upvotes: 0

Views: 4887

Answers (6)

Simon Smith
Simon Smith

Reputation: 246

You can do this by catching the exception in the BeginInvoke function and saving it in a temporary variable. After the await you can then rethrow it on the correct thread.

           Exception thrown = null;

        await Application.Current.Dispatcher.BeginInvoke(new Action<Action>(x =>
        {
            try
            {
                BackGroundMethod();
            }
            catch (Exception ex)
            {
                //exceptions on this thread MUST be caught or the program will exit
                //we will save it, and then when we are back on the main thread we will rethrow it
                thrown = ex;
            }
        }));

        if (thrown != null)
            throw thrown; //<--- I'm rethrowing it right here on the correct thread

Upvotes: 0

Ryan Amies
Ryan Amies

Reputation: 4922

When you use the Dispatcher to create a new thread, that thread has it's own stack, and so the exceptions wont bubble to the try...catch in TestMain, but instead they will originate in BackgroundMethod method. As you are throwing the exception in BackgroundMethod your try catch is useless, and so if you were to not throw the exception in BackgroundMethod your program wouldn't shut down.

private void BackGroundMethod()
{
    try
    {
        int a = 0;
        int b = 100;
        var error = b / a;
    }
    catch(Exception)
    {
        MessageBox.Show(ex.Message);
    }
}

Upvotes: 0

Klaus78
Klaus78

Reputation: 11896

You can catch all exceptions in all threads within the application by handling AppDomain.UnhandledException

Upvotes: 0

khellang
khellang

Reputation: 18102

Use Dispatcher.UnhandledException event to catch the exception in the TestMain() method like this:

Dispatcher.UnhandledException += (sender, args) => MessageBox.Show(args.Exception.Message);
ThreadPool.QueueUserWorkItem(ignore => Dispatcher.Invoke(new Action(BackGroundMethod)));

Edit: Remember to set the Handled property to prevent the internal exception handler from being called:

Dispatcher.UnhandledException += (sender, args) => 
{
    MessageBox.Show(args.Exception.Message);
    args.Handled = true;
}

Upvotes: 1

Jon
Jon

Reputation: 437336

To prevent the crash, add a try/catch around the operation which might throw:

ThreadPool.QueueUserWorkItem(x =>
{
    try {
        this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod()));
    }
    catch (DivideByZeroException ex) {
        // handle the error somehow
    }
});

However, think about what you are doing here: you are pushing some action to the thread pool, and in turn it pushes the same action to the dispatcher thread. Why not do this yourself directly without the QueueUserWorkItem call?

Upvotes: 0

Ekk
Ekk

Reputation: 5715

The reason is this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod)); executes asynchonously, so it will finished execute all code inside TestMain before BackGroundMethod is executed.

Upvotes: 1

Related Questions