How to catch an unhandled exception thrown from a sequel?

I cannot catch the unhandled exception that is selected from the continue task.

To demonstrate this problem, let me show you some code that works. This code applies to the underlying Windows Forms application.

Program.cs first :

using System;
using System.Windows.Forms;

namespace WindowsFormsApplication3
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

            Application.ThreadException += (sender, args) =>
            {
                MessageBox.Show(args.Exception.Message, "ThreadException");
            };

            AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
            {
                MessageBox.Show(args.ExceptionObject.ToString(), "UnhandledException");
            };

            try
            {
                Application.Run(new Form1());
            }

            catch (Exception exception)
            {
                MessageBox.Show(exception.Message, "Application.Run() exception");
            }
        }
    }
}

Subscribes to all available exception handlers. (Only Application.ThreadExceptionreally raised, but I wanted to make sure that I eliminated all the other possibilities.)

Now here is the form that causes the message to display correctly for the unhandled exception:

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            doWork();
            this.Close();
        }

        void doWork()
        {
            Thread.Sleep(1000); // Simulate work.
            throw new InvalidOperationException("TEST");
        }
    }
}

When you run this, after one second a message will appear with an exception message.

, "", , , .

, OnShown() :

protected override void OnShown(EventArgs e)
{
    base.OnShown(e);

    Task.Factory.StartNew(doWork).ContinueWith
    (
        antecedent =>
        {
            if (antecedent.Exception != null)
                throw antecedent.Exception;

            this.Close();
        },
        TaskScheduler.FromCurrentSynchronizationContext()
    );
}

, , - , program.cs.

, , , - .

- , , , ?


[EDIT]

await. , , , .Net 4.0. , , await , !

, - - , , .Net 4.5 :

protected override async void OnShown(EventArgs e)
{
    base.OnShown(e);
    await Task.Factory.StartNew(doWork);
    this.Close();
}

[EDIT2]

raidensan , , , . , . - antecedent => { throw antecedent.Exception; } , .

protected override void OnShown(EventArgs e)
{
    base.OnShown(e);

    var task = Task.Factory.StartNew(doWork);

    task.ContinueWith
    (
        antecedent => { this.Close(); },
        CancellationToken.None,
        TaskContinuationOptions.OnlyOnRanToCompletion, 
        TaskScheduler.FromCurrentSynchronizationContext()
    );

    task.ContinueWith
    (
        antecedent => { throw antecedent.Exception; },
        CancellationToken.None,
        TaskContinuationOptions.OnlyOnFaulted,
        TaskScheduler.FromCurrentSynchronizationContext()
    );
}
+4
4

, ( , - !)

BeginInvoke() . :

protected override void OnShown(EventArgs e)
{
    base.OnShown(e);

    Task.Factory.StartNew(() =>
    {
        try
        {
            doWork();
        }

        catch (Exception exception)
        {
            this.BeginInvoke(new Action(() => { throw new InvalidOperationException("Exception in doWork()", exception); }));
        }

        finally
        {
            this.BeginInvoke(new Action(this.Close));
        }
    });
}

, , , . . Evk !

+1

TaskScheduler.UnobservedTaskException . .

    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

        TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

        Application.ThreadException += (sender, args) =>
        {
            MessageBox.Show(args.Exception.Message, "ThreadException");
        };

        AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
        {
            MessageBox.Show(args.ExceptionObject.ToString(), "UnhandledException");
        };

        try
        {
            Application.Run(new Form1());
        }

        catch (Exception exception)
        {
            MessageBox.Show(exception.Message, "Application.Run() exception");
        }
    }
    private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        throw e.Exception;
    }

:

protected override void OnShown(EventArgs e)
{
    base.OnShown(e);
    Task.Factory.StartNew(doWork);
    Thread.Sleep(2000);
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

void doWork()
{
    Thread.Sleep(1000); // Simulate work.
    throw new InvalidOperationException("TEST");
}
+2

, , Application.ThreadException. Task (, Task.Exception, Task.IsFaulted ..) . , Task Control.Invok e , Control.Invoke , , Application.ThreadException, winforms , ( Control.BeginInvoke, Application.ThreadException).

, TaskScheduler.UnobservedTaskException .

, , - UnobservedTaskException, ThreadException. " ".

+2

, .Net 4.0. async/await, . Microsoft Async nuget.

enter image description here

Now modify OnShown as shown below (I added the code for doWorkit to be more obvious):

        protected async override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            await Task.Factory.StartNew(() => 
            {
                Thread.Sleep(1000); // Simulate work.
                throw new InvalidOperationException("TEST");
            })
                .ContinueWith
                (
                    antecedent => { this.Close(); },
                    CancellationToken.None,
                    TaskContinuationOptions.OnlyOnRanToCompletion,
                    TaskScheduler.FromCurrentSynchronizationContext()
                )
                .ContinueWith
                (
                    antecedent => { 
                        throw antecedent.Exception;
                    },
                    CancellationToken.None,
                    TaskContinuationOptions.OnlyOnFaulted,
                    TaskScheduler.FromCurrentSynchronizationContext()
                );

        }

Old answer Found solution, add task.Wait();. Not sure why this works:

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            var task = Task.Factory.StartNew(doWork)
                .ContinueWith
                (
                    antecedent => { this.Close(); },
                    CancellationToken.None,
                    TaskContinuationOptions.OnlyOnRanToCompletion,
                    TaskScheduler.FromCurrentSynchronizationContext()
                )
                .ContinueWith
                (
                    antecedent => { 
                        throw antecedent.Exception;
                    },
                    CancellationToken.None,
                    TaskContinuationOptions.OnlyOnFaulted,
                    TaskScheduler.FromCurrentSynchronizationContext()
                );

            // this is where magic happens.
            task.Wait();
        }
+1
source

Source: https://habr.com/ru/post/1659170/


All Articles