Sunday, March 20, 2011

How A Simple Bug Creates A Larger Problem: The Logoff Bug in Software Updater (Or: The Perils of Using e.Cancel in a FormClosing)

If you’re using Windows XP and have installed Project Nelson 1.0 Build 020 (which includes Software Updater), you may have noticed an inability to shutdown or log off your computer. Before you ask, an updated version of Software Updater without the bug is on the way has been released, along with a bunch of optimising and code cleanup (but that’s mainly under the hood.) You should get a notification through Software Updater when that’s ready, but in the meantime you can launch Task Manager and, under the Processes tab, select SoftwareUpdater.exe and hit “End Process”. You should be able to log off then.
So, how did I screw up a program so massively for it to prevent shutdown? Actually, it was really simple. For developers, this is a warning message about the problems of cancelling the closing of your window without taking all things in account.

The Problem

Software Updater consists of a few forms, but the important one is the main window. On the main window is a NotifyIcon object, the object which I can use to give my program a constant presence in the notification area.
(This is a bad idea for reasons Raymond Chen lists, but really, that’s beside the point, and the same problem would exist anyway, since that form also contains the code to check updates. I will be removing the superfluous icon in a later version anyway, so, yuh.)
Now, I wanted to keep the form open when someone clicked the close button, so it could do its business. I’d hide it from the user, but it would still be there, running the stuff it needed to run.
After a poke around the form’s FormClosing event, I decided to use the following code:
Private Sub Main_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    e.Cancel = True
    Me.Visible = False
End Sub
See the problem yet?
The problem is that every time anything tries to close the form, whether it’s hidden or not, this code will execute. The window will say “De-nied!” and hide the window. Including when the computer tries to, say, shut down.
I’d like to note this isn’t a problem in Windows Vista or above, which ignores the app saying “De-nied!” and punches its lights out closes it anyway. But really, you shouldn’t be lazy and rely on the operating system to do your job. (I have an excuse – my main computer is Windows Vista, so I had to borrow another person’s computer to test it out on, and even then I thought I’d messed something up in the installer script, not the program itself.)

The Solution

Through persistent Googling of the problem (as, once I’d figured out the problem went away after you killed Software Updater, on a hunch I tried searching for variations of e.Cancel shutdown problems) I found what I was looking for.
It turns out that during my poking around, somehow I’d missed that FormClosing had an e.CloseReason variable. There are quite a few options to choose from, but the one we’re looking for is CloseReason.WindowsShutDown. To wit:
Private Sub Main_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    If Not e.CloseReason = CloseReason.WindowsShutDown Then
        e.Cancel = True
        Me.Visible = False
    End If
End Sub

And that, my friends, is that. Later days, and may you not make the same mistake I did!

No comments:

Post a Comment