1

I'm wanting to do some cleanup during the FormClosing event of a WinForm in a WinForms application. The cleanup code is async.

The FormClosing hander is:

private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    await DoCleanupAsync();

    // we never get here as the Form is disposed almost as soon as the async cleanup-method executes;
}

, with:

private async Task DoCleanupAsync()
{
    Console.WriteLine("stopping...");  // This is output
    await Task.Delay(2000);
    Console.WriteLine("stopped");      // This is not
}

What's happening is the Form is being disposed of before the await on the cleanup code is complete.

Incidentally, this is causing a race condition where I'm trying to update the form (for simplicity let's say the form contains log output) during the cleanup and I get an ObjectDisposedException because the form has been disposed.

If I look at the callstack when Dispose() is called, I can see it's being triggered from

System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) Unknown

, so we can see that the dispose is being triggered directly from the FormClosing event, and nothing that I'm doing myself.

I can work around this by doing something hacky like:

private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    e.Cancel = true;
    await DoCleanupAsync();
    this.FormClosing -= Form1_FormClosing;
    Close();
}

, but I'd like to understand what's actually going on!

Ive
  • 457
  • 5
  • 19
  • Have you tried setting the flag on `e` to prevent closing before calling `DoCleanupAsync()`? – DiskJunky Sep 22 '17 at 14:08
  • 1
    Of course. As soon as you reach _await_, the function returns and execution continues from where `Form1_FormClosing` was called. You seem to be thinking that execution halts until `DoCleanupAsync();` finishes, but that is not what `await`means. – oerkelens Sep 22 '17 at 14:09
  • You should read more about how that `async/await` stuff works. Then you'll find an answer by yourself. – dymanoid Sep 22 '17 at 14:10
  • 1
    The problem is that the event cannot / will not be awaited. So even if `DoCleanupAsync` is still running, the event has completed as far as the runtime is concerned. So the lifecycle of the form is over. – Peter Bons Sep 22 '17 at 14:12

1 Answers1

2

what's actually going on

When you await an async method, the control flow returns to the caller of your Form1_FormClosing method. And when you didn't set the e.Cancel flag at this time, the form is closed.

The execution of your handler is resumed when the Task returned by DoCleanupAsync completed. But then the form is already closed.

René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • Thanks -- silly me. I was assuming that the 'caller' of the Form-closing event would also be awaiting -- but that's obviously not possible given the return-type of void – Ive Sep 22 '17 at 14:31