2

First of all, let me say that, it's so hard to explain my problem in detail, but I will try my best. I will update with a detailed explanation or more codes which is used by me that probably caused the exception. And I'm sorry if my code is messy.

There are many SO questions with the same title that I have read, but I have no luck at all. I have a very little understanding of Thread/Task/Dispatcher here, so please guide me if you find something wrong about my code.


Intoduction

My application does the background task by timer every n minutes.

The background task: fetches data from API, then generates Window element as a form to contain the data, then prints them.

The problem: The exception has already occured twice at the moment, which prevents two form documents to be generated so they are not printed.

The detailed exception which is get from TaskScheduler.UnobservedTaskException is:

  • A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

  • Stack Trace: N/A
  • Inner Exception:

    System.Collections.ObjectModel.ReadOnlyCollection`1[System.Exception]


Here is my piece of code that may be useful for you to find the source of the problem:

public void BackgroundTask(object sender, EventArgs e)
{
    Application.Current.Dispatcher.Invoke(
        new Action(GetInvoiceData),
        DispatcherPriority.Background,
        null
    );
}

...where GetInvoiceData is:

public async void GetInvoiceData()
{
    try
    {
        JsonData = await ApiHelperInstance.Post(ApiParam);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    finally
    {
        if (!string.IsNullOrEmpty(JsonData))
        {
            var apiReturn = new ApiReturn();

            try
            {
                apiReturn = JsonConvert.DeserializeObject<ApiReturn>(JsonData);
            }
            catch (JsonException ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (apiReturn.Result != null)
                {
                    foreach (ApiResult apiResult in apiReturn.Result)
                    {
                        InvoiceQueue.Enqueue(new Invoice(apiResult));
                    }

                    var worker = new BackgroundWorker();
                    worker.DoWork += GenerateDocumentAndPrint;
                    worker.RunWorkerAsync();
                }
            }
        }
    }
}

...and GenerateDocumentAndPrint is:

public void GenerateDocumentAndPrint(object sender, DoWorkEventArgs e)
{
    while (InvoiceQueue.Count != 0)
    {
        Dispatcher.Invoke(() =>
        {
            Invoice invoice = InvoiceQueue.Dequeue();

            var invoiceForm = new InvoiceForm();
            var shippingLabelForm = new ShippingLabelForm();

            invoiceForm.Dispatcher.Invoke(async () =>
            {
                var invoiceTmp = invoice;
                var invoiceDoc = new FixedDocument();

                try
                {
                    invoiceDoc = await invoiceForm.CreateDocument(invoiceTmp);
                }
                finally
                {
                    InvoiceDocumentName = PrintJobNameSub + " - Invoice #" + invoice.TransOrder.TransNumber;
                    PrintHelperInstance.SetPrinterByName(InvoicePrinterName);
                    PrintHelperInstance.PrintDocument(invoiceDoc.DocumentPaginator, InvoiceDocumentName);
                    invoiceForm.Close();
                }
            }, DispatcherPriority.ContextIdle);

            shippingLabelForm.Dispatcher.Invoke(async () =>
            {
                var invoiceTmp = invoice;
                var shippingLabelDoc = new FixedDocument();

                try
                {
                    shippingLabelDoc = await shippingLabelForm.CreateDocument(invoiceTmp);
                }
                finally
                {
                    ShippingLabelDocumentName = PrintJobNameSub + " - Shipping Label #" + invoice.TransOrder.TransNumber;
                    PrintHelperInstance.SetPrinterByName(ShippingLabelPrinterName);
                    PrintHelperInstance.PrintDocument(shippingLabelDoc.DocumentPaginator, ShippingLabelDocumentName);
                    shippingLabelForm.Close();
                }
            }, DispatcherPriority.ContextIdle);
        }, DispatcherPriority.Normal);
    }
}

...and async method CreateDocument from both of InvoiceForm and ShippingLabelForm contains await Task.Delay(delay).


Is there any mistake I made from my code? Is it caused by wrong use of Dispatcher.Invoke? Is it caused by wrong use of DispatcherPriority enum? Is it something wrong with the Task.Delay operation?

Rizki Pratama
  • 551
  • 4
  • 23
  • What if you catch any exception thrown by invoiceForm.CreateDocument(invoiceTmp) ? – mm8 Mar 07 '17 at 11:54
  • @mm8 I've already catched all unhandled exception, the one I mentioned is the only one that got catched. – Rizki Pratama Mar 07 '17 at 11:58
  • You have no catch clause around the call to await invoiceForm.CreateDocument(invoiceTmp). – mm8 Mar 07 '17 at 12:02
  • 1
    Just a finally. – mm8 Mar 07 '17 at 12:02
  • Is there a reason to use the Dispatcher in that way? Why not TPL? – Fildor Mar 07 '17 at 12:11
  • I don't know about all these dispatch calls, but you can at least check the BackgroundWorker for errors, like here: http://stackoverflow.com/questions/1044460/unhandled-exceptions-in-backgroundworker – AndersJH Mar 07 '17 at 12:53
  • You shouldn't ever get that error from `DispatcherUnhandledException`. Are you sure you're not seeing it at `TaskScheduler.UnobservedTaskException`? Are you setting `` in your config? – Stephen Cleary Mar 07 '17 at 14:47
  • @mm8 Okay I've added the catch block there, wait until I get the exception from there. – Rizki Pratama Mar 08 '17 at 01:44
  • @StephenCleary Upss, my bad, the log strings are swapped :D Yes, you're right, it came from `TaskScheduler.UnobservedTaskException`. – Rizki Pratama Mar 08 '17 at 01:46
  • Your inner exception is a collection of exceptions, `foreach` over the `InnerExceptions` property and call `.ToString()` on all of them and print those out. – Scott Chamberlain Mar 08 '17 at 07:03
  • Possible duplicate of [A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was](https://stackoverflow.com/questions/7883052/a-tasks-exceptions-were-not-observed-either-by-waiting-on-the-task-or-accessi) – d.i.joe Oct 09 '18 at 14:33

1 Answers1

6

TaskScheduler.UnobservedTaskException gets exceptions when a task's exceptions are not observed. If you await all your tasks, then this event will never fire.

Note that this event does not necessarily mean an error in the strict sense of the term. For example, this can happen if you abandon a task - fairly common if your code contains any Task.WhenAny calls. This can also happen if a "fire and forget" task throws an exception. In both of these cases, it's not actually an error. In the WhenAny case, a different task already completed the Task.WhenAny, so you don't care if another task threw an exception. In the case of "fire and forget", "forget" literally means "I don't care about exceptions", so you shouldn't care if it threw an exception.

This event only indicates an error if you are accidentally missing an await. The easiest way to find a missing await is by examining the call stack of the inner exceptions and then examining the callers of that method, etc., until you find the one that is not properly awaiting the task.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • _"If you `await` all your tasks, then this event will never fire"_ I'm doing `HttpResponseMessage response = await httpClient.GetAsync(Utils.serverHost + url);` and I still get that event on that line if the server isn't running and can't be connected to – Clonkex Apr 08 '22 at 01:39
  • Please post a minimal repro as a separate question. – Stephen Cleary Apr 08 '22 at 01:54