1

No matter how a winforms application closes the FormClosing event always reports the closereason as "userclosing"

My application needs to log how a it was closed. Since the FormClosingEventArgs in the FormClosing event supports enumerated closing values such as

  • WindowsShutdown
  • TaskManagerClosing
  • UserClosing
  • MdiFormClosing
  • None

it makes sense to simply log that value as the form shuts down. But... no matter how I close the form it always reports CloseReason.UserClosing. I have already tried using reflection (based on the suggestion in Task manager close is not detected second time in a WinForms Application ) - but I get the same results as reported by FormClosingEventArgs

I wrote a test app

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        Dim fi As FieldInfo
        Dim ty As Type = GetType(Form)
        fi = ty.GetField("closeReason", BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Public)
        Dim cr As String = fi.GetValue(Me).ToString()

        My.Computer.FileSystem.WriteAllText("d:\closetest.log", cr & vbCrLf, True)
        My.Computer.FileSystem.WriteAllText("d:\closetest.log", e.CloseReason.ToString & vbCrLf, True)


    End Sub

The above code always reports UserClosing in both the FormClosingEventArgs as well as in the reflected value. It doesn't matter how I close the form - including closing via the TaskManager. This is really frustrating - it's as though MS has turned this capability off in the OS altogether. My issue is that I have an application that appears to close on it's own and leaves no trace in my own logging, nor in the Windows Application logs - nothing, nada. So I am suspicious of some external event and need to see if this might log it...

Jaskier
  • 1,075
  • 1
  • 10
  • 33
Destek
  • 154
  • 1
  • 10
  • If you call `Appliction.Exit()`, you should see `ApplicationExitCall`. If you use TaskManager, you see nothing (your app is killed). If you close the owner Form, you should see `FormOwnerClosing`. If you end the Windows session... How are you closing that Form? – Jimi Sep 06 '19 at 15:36
  • BTW, reflection here is not really useful. Read the value returned in `FormClosingEventArgs` – Jimi Sep 06 '19 at 16:00
  • Thanks - yes. I was using me.close in a button. The result for that was UserClosing (which I expected). I tried Application.Exit as you suggest and indeed I do get ApplicationExitCall from the FormClosingEventArgs. I get None from reflection so your point about it not being useful is a good one. What I am most interested in is when Windows is shutting down or someone kills it from the taskmanager. Both of those report (in my log) as UserClosing... If I saw nothing when killed from the taskmanager (as you indicate) that would actually be useful, but instead it reports UserClosing. – Destek Sep 06 '19 at 16:10
  • `CloseReason.TaskManagerClosing` is only set when the Form receives a `WM_CLOSE` and `CloseReason = CloseReason.None` (the property, here, not the Field). Note that the public `Close()` method sends a `WM_CLOSE` message, but `CloseReason` is already set to `UserClosing`. TaskManager kills an application, so the FormClosing event is never raised. If you're recording something, it's a previous state. The other CloseReason(s) work as documented. – Jimi Sep 06 '19 at 16:19
  • The note about TaskManager doesn't apply to very old Windows systems (pre-Vista? I don't remember when the TaskManager's *behaviour* was changed). Note that the answer you linked is almost 10 years old. – Jimi Sep 06 '19 at 16:32
  • This is just normal, they were a bit over-enthusiastic when they added this in .NET 2.0. WPF scaled it back again. WindowsShutdown is reliable, TaskManagerClosing can't work anymore since XP, nobody uses MDI so that leaves UserClosing. If you need a "I closed it myself" indication then just use a bool variable that you set to *true* before the Close() call. – Hans Passant Sep 06 '19 at 17:29
  • BTW2, you can get a `TaskManagerClosing` result if you send ([SendMessage](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage)) a `WM_CLOSE` message to your app from another. The Form that receives the event is the current Active Form. If you have a child Form opened (`.Show(this)`), the child Form will register `FormOwnerClosing`, while the owner will report `TaskManagerClosing`. – Jimi Sep 06 '19 at 17:58
  • Yeah - I'm coming to the conclusion this is just vestigial functionality that was likely deprecated some time ago. It's unfortunate because it would be useful information in certain circumstances. That link I provided being 10-years-old pretty much says it all... Thanks everyone - all your comments were helpful even if I didn't get the answer I was hoping for! – Destek Sep 06 '19 at 17:58
  • Nope. It's not a deprecated functionality. Just the TaskManager `Terminate/End Process` behaviour has changed over time. The other states work as intended. You just need to know what they're for and when each of them is set. If you want to use the CloseReason for a specific, well, reason, you need to talk about that reason. All the logging work, except when the TaskManager is involved. If you see a `CloseReason.TaskManagerClosing`, you know another app has closed yours (or, is asking to). Not the TaskManager. – Jimi Sep 06 '19 at 18:06

2 Answers2

1

You can do like this:

Dim CloseByMyButton As Boolean = False

Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
    'If this form is child form call this   
    CloseByMyButton = True
    me.Close 

    'if this form is main form call this
    'Application.exit
End Sub

Private Sub MessageForm_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    'may be e.CloseReason = CloseReason.TaskManagerClosing must first line in this sub procedure, because it is too quick 
    If e.CloseReason = CloseReason.TaskManagerClosing Then
        MessageBox.Show("You close from Task Manager")
    ElseIf e.CloseReason = CloseReason.ApplicationExitCall Then
        MessageBox.Show("You close by exit application")
    ElseIf e.CloseReason = CloseReason.UserClosing Then
        If CloseByMyButton = True Then
            MessageBox.Show("You close by UserClosing With btnClose_Click")
        Else
            MessageBox.Show("You close by UserClosing Normally")
        End If
    Else
        MessageBox.Show("You close by Others")
    End If

End Sub
user11982798
  • 1,878
  • 1
  • 6
  • 8
  • 1
    Not a good idea to call an event. Also, in the FormClosing event you call Close(). Guess what happens. – Mary Sep 08 '19 at 00:03
  • I don't know, but MessageForm_FormClosing and Me.Dispose() will twice perform, usually without me.dispose in form_closing. Normaly in btnClose I put me.close and me.dispose, but sometimes, I want to cancel if not from button_close, so I make code like above, please your suggestion for me to next better – user11982798 Sep 08 '19 at 00:15
  • thanks for reminding me, now I can call application.exit, and in MessageForm_FormClosing, I can get e.CloseReason = CloseReason.ApplicationExitCall. So I don't need again to call MessageForm_FormClosing event. – user11982798 Sep 08 '19 at 02:36
  • Check the comment by @Hans Passant. Also notice he has 812,961 reps. This is someone to listen to. – Mary Sep 08 '19 at 03:29
  • Thanks, I always want to study about vb.net, because I am newbie in vb net, before I use vb6 until 2018, sometimes, I learn knowledge by answer, and read from any one's answer. And many appreciation for your suggestion. – user11982798 Sep 08 '19 at 03:40
  • Mary - I agree. Hans has provided solid advice before. I think there is enough good advice to glean from this that it has allowed me to get enough meaningful information upon exit as possible. – Destek Sep 09 '19 at 11:40
  • Hi user119... Your code works as long as I disable the winform X for closing (otherwise there is no difference between that and TaskManagerClosing). To do that I used suggestions from this old post (but still works well): https://stackoverflow.com/questions/1743433/visually-remove-disable-close-button-from-title-bar-net – Destek Sep 09 '19 at 14:17
  • @Mary Nevertheless, there is some more; tested both on Windows 11 and on Windows Server 2019. If the application is closed by the Task Manager, the reason is *UserClosing*, but if the application is closed by the *Task Scheduler*, the reason is *TaskManagerClosing*. How can the Task Scheduler close an application? By setting a maximum run time in the start trigger. – Giorgio Barchiesi Jun 06 '23 at 07:48
0

I have experimented a little on Windows 11 and on Windows Server 2019. Here is what I have found out:

  • All user-triggered close events (including close via Task Manager) --> UserClosing
  • Windows shutdown --> WindowsShutdown
  • Closed by Task Scheduler --> TaskManagerClosing

An application can be closed by the Task Scheduler if in the start trigger a maximum running time is specified.

A practical example: in most of my applications I ask the user for confirmation when the application is about to be closed (FormClosing event), but if the CloseReason is WindowsShutdown or TaskManagerClosing, I close the application without further ado. So the application does not hinder the operating system when necessary.

Giorgio Barchiesi
  • 6,109
  • 3
  • 32
  • 36