1

I have to call an API when my WPF Applications is being closed.

I'm using Application_Exit event because I have to do this even closing all the windows, being shut down from Task Manager or Alt + F4.

I've tried several ways, but all the options has some issues (some of them are these):

OPTION A: Async event + await

private async void Application_Exit(object sender, ExitEventArgs e)
{
    if (MonitorInstance.Instance.User != null)
    {
        Log.Information($"Disconnect starting");
        var result = await _apiClient.Disconnect();
        Log.Information($"Disconnect result: {result}");
    }
}

This option just calls the HTTP CONNECT (which sometimes succeed and others is being cancelled) but not my PATCH request.

OPTION B: Blocking Thread with Result of Task.

private void Application_Exit(object sender, ExitEventArgs e)
{
    if (MonitorInstance.Instance.User != null)
    {
        Log.Information($"Disconnect starting");
        var result = _apiClient.Disconnect().Result;
        Log.Information($"Disconnect result: {result}");
    }
}

This effectively calls the API but the process continues running. Visual Studio continues showing that the App is running and this is not desired.

I think it's worth to mention that I'm calling the API (which uses SSL) using HttpClient which is being created in the Disconnect method and I'm using .NET Core 3.1.

My questions:

  1. Is Application_Exit the proper event to do this?
  2. How to program the Application_Exit event to fulfill all the scenarios?
  3. Should I change the implementation for my call to do it directly without the HttpClient?
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • Maybe there's an `await` without `ConfigureAwait(false)` in your API client? – Kevin Gosse Oct 10 '20 at 13:16
  • @Marcos Frank - [Handle a WPF Exit Event](https://stackoverflow.com/questions/7717222/handle-a-wpf-exit-event) – Jackdaw Oct 10 '20 at 13:46
  • @KevinGosse thanks for your response. I'm not using ConfigureAwait(false). – Marcos Frank Oct 10 '20 at 13:54
  • @Jackdaw thanks for your response. So you're suggesting me Stop all my windows for closing (or detect if all of them were closed), make the API call and then closing my application? I was thinking in a more centralized solution. But I will try that. – Marcos Frank Oct 10 '20 at 13:58
  • @MarcosFrank Well you should, you're running into a deadlock. – Kevin Gosse Oct 10 '20 at 15:25

1 Answers1

1

This a classic case of deadlock. Your code is running under a synchronization context, your await calls inside of _apiClient.Disconnect() try to resume on the UI thread, but the UI thread is blocked waiting synchronously on the task because of .Result.

There are multiple ways to get around that, but for your precise case I don't think it's useful to get fancy. Just use Task.Run to run your call in a safer context:

private void Application_Exit(object sender, ExitEventArgs e)
{
    if (MonitorInstance.Instance.User != null)
    {
        Log.Information($"Disconnect starting");
        var result = Task.Run(apiClient.Disconnect).Result;
        Log.Information($"Disconnect result: {result}");
    }
}
Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94