Bug in FormClosingEventArgs.CloseReason?

.NET Reflector is your friend when working out how WinForms is operating.

The Form class has an internal field called closeReason and this is used when generating the event parameter that you examine in the Closing event. This internal field is set in four different places that I can find. These are...

1, The Form.Close() method sets the closeReason = UserClosing.

This makes sense as making a manual call to the Form.Close() method is usually the result of some user action, such as a File->Exit menu option being selected by the user. Clearly this is a user action.

2, The WM_SYSCOMMAND (SC_CLOSE) sets the closeReason = UserClosing.

The WndProc of the Form processes the SC_CLOSE system command by setting the closeReason to UserClosing and the lets the default window proc execute and close the application. This makes sense as this SC_CLOSE is sent when the user presses the window close chrome button or selected the close option from right clicking the title bar. Both are user actions and so setting the closeReason to UserClosing appears correct.

3, WndProc processes message WM_CLOSE (0x10) with closeReason = TaskManagerClosing

WM_CLOSE is sent by task manager and other applications to close a window and if the closeReason is currently equal to None it updates it to TaskManagerClosing. Note this issue with it being updated only if it is None as I think this is a problem for you.

4, WndProc processes messages 0x11 and 0x16 with closeReason = WindowsShutDown

This is not very interesting as you do not care about this scenario but it is just standard processing of shut down messages.

So the core problem you are having is that at no point is the closeReason being reset back to None when you cancel the Closing event. Therefore point number 3 above will never correctly update the value to TaskManagerClosing if that occurs after your cancel. As the closeReasson is an internal field you cannot update it directly. But you can cheat and this is an approach I have used myself in the past. You need to use reflection to get access to the internal field and then reset it to None when you set Cancel=true in your event handler.

I have not tested this code but you need something along the lines of...

PropertyInfo pi = typeof(Form).GetProperty("CloseReason",
                                           BindingFlags.Instance |
                                           BindingFlags.SetProperty |
                                           BindingFlags.NonPublic);

pi.SetValue(this, CloseReason.None, null);

Tags:

C#

Winforms