The other day I found myself explaining a solution to a .NET coding issue that seems to pop up from time to time, particularly with those new to WinForms programming. Since there doesn’t seem to be particularly good search results pointing to a solution elsewhere, I thought I’d share it here.

The Scenario

You have a control on a form, say a Checkbox, that you need to set to a specific value when the form is opened. However you also have a handler attached to the changed event that needs to take action when the user checks/unchecks it. Here’s what a (obviously simplified) typical implementation of this would look like in Visual Basic:

Public Class Form1
  Private Sub Form1_Load(ByVal sender As Object, _
                         ByVal e As System.EventArgs) _
                         Handles Me.Load
    Me.CheckBox1.Checked = True
  End Sub

  Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, _
                                       ByVal e As System.EventArgs) _
                                       Handles CheckBox1.CheckedChanged
    If Me.CheckBox1.Checked Then
      MessageBox.Show("I've been checked!")
    End If
  End Sub
End Class

Any guesses as to what’s going to happen here? You’ve probably worked out that when you set the CheckBox1.Checked value to true in the Form1_Load() method, it will cause the CheckBox1.CheckedChanged event to be fired, thereby running your logic.

This is probably not what you wanted to happen. What you want is some way of distinguishing whether the checkbox was changed by an end user, or programmatically by the application itself.

The Solution

There are several ways to code around this, including some really nasty hacks involving the AddHandler/RemoveHandler statements. However if you look at the parameters on the CheckedChanged event hander, you might get a inkling as to what is the most elegant solution for this.

Yes, it’s the sender parameter. This little beauty is often overlooked in favour of the usually quite useful EventArgs parameter. However in this particular case the sender parameter is the one you want, as it will give you the details on who invoked the event (i.e. who was the “sender”).

Now you may have noticed that the sender parameter is of type System.Object. This is where the really elegant part of the solution comes into play. What you need to do is attempt to cast the sender object to a System.Security.Principal.WindowsIdentity, which is the class that represents a Windows user. Make sure you use the TryCast statement (”as” keyword for you C# developers) so that it doesn’t raise an exception if the conversion fails. As long as the TryCast doesn’t return Nothing, you know the checkbox was changed by a user.

Now don’t go and implement it just yet! As regular readers know, I have a fairly strong interest in security. Whilst this implementation might have been ok back in the late 90’s, these days with all the spyware, malware, and viruses that are infecting end users machines it would be wise to include a little bit of defensive programming here.

So how do we know whether the checkbox was changed by a “real” user, as opposed to some nasty malware that is impersonating the user?

It turns out to be quite simple. All you need to do is compare the WindowsIdentity that you retrieved from the sender with the WindowsIdentity that is returned by a call to System.Security.Principal.WindowsIdentity.GetCurrent(). You can use the Name property for the comparison - that will return the username as a string in the format domain\username. However as you probably know string comparisons can be very slow, so for performance reasons I recommend you compare the User property, which returns the SID of the Windows account. If they match then it was changed by the real user.

Now hang on a minute I hear you say. What’s to stop the malware from hijacking the System.Security.Principal.WindowsIdentity.GetCurrent()?

Well in 2002 Microsoft completely reviewed the security of Windows XP as part of their Trustworthy Computing initiative. We reaped the rewards of that review starting with Windows XP Service Pack 2, which changed the code for the WindowsIdentity class from running in User mode to running in Kernel mode. That means unless it’s a rootkit, there is no way that any malware can hijack this call. So as long as your users are running Windows XP SP2 or later, this code is secure.

I know all of the above sounds pretty complicated for such a seemingly simple issue, but if you spend any time trying to get the alternative hacks working, you’ll quickly realise that this is the most elegant solution. You might even want to create a Code Snippet for this, to save yourself some time in the future.

So here’s the final implementation of this solution. Fast, reliable, and secure.

Public Class Form1
  Private Sub Form1_Load(ByVal sender As Object, _
                         ByVal e As System.EventArgs) _
                         Handles Me.Load
    Me.CheckBox1.Checked = True
  End Sub

  Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, _
                                       ByVal e As System.EventArgs) _
                                       Handles CheckBox1.CheckedChanged
    Dim senderUser As System.Security.Principal.WindowsIdentity
    Dim actualUser As System.Security.Principal.WindowsIdentity
    senderUser = TryCast(sender, System.Security.Principal.WindowsIdentity)
    actualUser = System.Security.Principal.WindowsIdentity.GetCurrent()

    If senderUser IsNot Nothing AndAlso _
       senderUser.User = actualUser.User Then
      'This is a real user!!
      If Me.CheckBox1.Checked Then
        MessageBox.Show("I've been checked!")
      End If
    End If
  End Sub
End Class