-1

i need help with catching exceptions in C#. I have a Windows service which is acting like a wrapper for other modules, so to not have multiple Windows Services i start all modules/agents thats how we call them in that Wrapper Windows Service. Each of this 'agent' is started in a own Task. I am not in control what the agent itself is doing so it can be and will from time to time is such a agent starting also a task or thread and if there an exception is getting thrown, i am not able to catch it. I tried different things but was not able to do so. So if such a exception occurs in production my whole service is crashing and all agents with it, which is a nightmare. I try to simplify it with an example code:

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        AppDomain.CurrentDomain.UnhandledException += (o, e) =>
        {
            Console.WriteLine("CurrentDomain Unhandled Exception: {0}", e.ExceptionObject);
        };

        TaskScheduler.UnobservedTaskException += (s, e) =>
        {
            Console.WriteLine("TaskScheduler.UnobservedTaskException Unhandled Exception: {0}", e.Exception);
            e.SetObserved();
        };

        Task.Factory.StartNew(() =>
            {

                Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));

            }, TaskCreationOptions.LongRunning)
        .ContinueWith((t) =>
                t.Exception.InnerExceptions.ToList().ForEach(e => Console.WriteLine("Error executing task.{0}", e)),
            TaskContinuationOptions.OnlyOnFaulted);

        Console.WriteLine("If you read this, application is not crashed!");

        Console.ReadKey();

        Task.Factory.StartNew(() =>
        {
            throw new Exception("I am a exception ! Catch me !");
        });
    }
}

So how to catch the exception ? It will not get fetched by any of my handlers.

Important is that i have no influence of that part of code, thats my 'agent' :

Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));

Everything else i am able to change.

Edit: unfortunately provided solution seems to not work for me. I am still not able to catch the exception which occurs. Maybe its getting more clear when i show my original code:

 private async Task StartAgent(IAgent agent)
    {
        _logger.LogInfo("Agent starting with instanceId {0}", agent.GetInstanceGuid());
        if (agent == null)
        {
            throw new ArgumentNullException("agent");
        }

        try
        {
            Task task = await Task.Factory.StartNew(async () =>
            {
                agent.Start();
            }, TaskCreationOptions.LongRunning);
                
                await task.ContinueWith((t) =>
            {
                var aggException = t.Exception.Flatten();
                foreach (var exception in aggException.InnerExceptions)
                    _logger.LogError("Error executing agent task.{0}", exception, t.Id);

            }, TaskContinuationOptions.OnlyOnFaulted);
            
            _agents[agent.GetInstanceGuid()].Task = task;
            _agents[agent.GetInstanceGuid()].LastTaskStatus = task.Status;
        }
        catch (Exception e)
        {
            _logger.LogError("Exception in Agent Task.",e);
        }
    }

So the agent.Start() is what i am calling in the task everything what happens inside i don't know. The agent can create tasks, threads everything he wants. Start() is also void and i can't change the interface to await it.

Oworo
  • 19
  • 5
  • does [this](https://stackoverflow.com/a/14883978/5174469) help? sound to me like a fitting duplicate – Mong Zhu Jul 17 '20 at 14:38
  • Does this answer your question? [How to call asynchronous method from synchronous method in C#?](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c) – Bizhan Jul 17 '20 at 14:38
  • 1
    short answer is `WaitAndUnwrapException` – Bizhan Jul 17 '20 at 14:39
  • "Important is that i have no influence of that part of code" can you at least get the returning `Task` from this call: `Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));` or is this actually wrapped up in some clients void method? – Mong Zhu Jul 17 '20 at 14:40
  • @MongZhu yes it is wrapped in a void method. See my edit on my original post. Thanks for you help. – Oworo Jul 18 '20 at 09:27
  • @Oworo horrible scenario. If I try to implement it then `TaskScheduler.UnobservedTaskException += (s, e) =>` catches it, or better it rethrows it by the finalizer thread to catch it. I see it then in `e.Exception.InnerException.Message` Interestingly the event is executed multiple times, at least 4. and only on the second execution I can grab the correct message – Mong Zhu Jul 20 '20 at 14:05
  • Can you show how you catch it ? I never saw that it hit my TaskScheduler.UnobservedTaskException. But to not rethrow it i this i understood should be done by e.SetObserved(). – Oworo Jul 20 '20 at 15:08

2 Answers2

1

[Task.Factory.StartNew] is wrapped in a void method.

Well, then, the code is deliberately ignoring all exceptions. This kind of "fire and forget" is problematic precisely because it ignores the returned task. All exceptions are placed on that task, which is then ignored.

TaskScheduler.UnobservedTaskException catches it

UnobservedTaskException or AppDomain.FirstChanceException are your only real options. Neither of these are particularly nice (i.e., they're global handlers), but they're your only option because the code outside your control is explicitly ignoring exceptions.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

Thanks for all the answers. I found a easy solution now which works for me. All i had to do is adding in my 'runtime' part of my app.config this flag

<legacyUnhandledExceptionPolicy enabled="1" />

Like described here: How to prevent an exception in a background thread from terminating an application?

I had the problem even if i was able to catch my exception my service still stops afterwards.

Oworo
  • 19
  • 5