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!
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.