Welcome to MSDN Blogs Sign in | Join | Help

Receiving Session Lock and Unlock Notifications

Some programs, such as MSN Messenger, change their behavior when the current session is locked and unlocked.  Messenger, for instance, will change your status to Away while your machine is locked, and then back to Online when your machine is unlocked.

In order to pull this off, you'll need Windows to notify your application when the locked status of the current session is changing.  On Windows XP and higher, you can get this notification via a WM_WTSSESSION_CHANGE message.  You notify Windows that you want to receive that message via a call to WTSRegisterSessionNotification  (which requires that you make a matching call to WTSUnRegisterSessionNotification when you no longer need notification).  Both of these APIs are declared in the WtsApi32.h header in the platform SDK.

When the WM_WTSESSION_CHANGE message arrives, there is a status code in the wParam indicating what type of change is happening.  The two changes we care about are WTS_SESSION_LOCK and WTS_SESSION_UNLOCK to represent locking and unlocking the machine.

All of the APIs involved are relatively simple, and creating P/Invoke signatures for them is easy.  Once that work is done, creating a simple base class that is capable of receiving lock and unlock notifications is very straightforward.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

/// <summary>
/// Base class for a form that wants to be notified of Windows
/// session lock / unlock events
/// </summary>
public abstract class LockNotificationForm : Form
{
    // from wtsapi32.h
    private const int NotifyForThisSession = 0;

    // from winuser.h
    private const int SessionChangeMessage = 0x02B1;
    private const int SessionLockParam = 0x7;
    private const int SessionUnlockParam = 0x8;

    [DllImport("wtsapi32.dll")]
    private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags);

    [DllImport("wtsapi32.dll")]
    private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);
    
    // flag to indicate if we've registered for notifications or not
    private bool registered = false;
    
    /// <summary>
    /// Is this form receiving lock / unlock notifications
    /// </summary>
    protected bool ReceivingLockNotifications
    {
        get { return registered; }
    }
            
    /// <summary>
    /// Unregister for event notifications
    /// </summary>
    protected override void Dispose(bool disposing)
    {
        if(registered)
        {
            WTSUnRegisterSessionNotification(Handle);
            registered = false;
        }
        
        base.Dispose(disposing);
        return;
    }
    
    /// <summary>
    /// Register for event notifications
    /// </summary>
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);

        // WtsRegisterSessionNotification requires Windows XP or higher
        bool haveXp =   Environment.OSVersion.Platform == PlatformID.Win32NT &&
                            (Environment.OSVersion.Version.Major > 5 || 
                                (Environment.OSVersion.Version.Major == 5 &&
                                 Environment.OSVersion.Version.Minor >= 1));

        if(haveXp)
            registered = WTSRegisterSessionNotification(Handle, NotifyForThisSession);

        return;
    }

    /// <summary>
    /// The windows session has been locked
    /// </summary>
    protected virtual void OnSessionLock()
    {
        return;
    }

    /// <summary>
    /// The windows session has been unlocked
    /// </summary>
    protected virtual void OnSessionUnlock()
    {
        return;
    }
    
    /// <summary>
    /// Process windows messages
    /// </summary>
    protected override void WndProc(ref Message m)
    {
        // check for session change notifications
        if(m.Msg == SessionChangeMessage)
        {
            if(m.WParam.ToInt32() == SessionLockParam)
                OnSessionLock();
            else if(m.WParam.ToInt32() == SessionUnlockParam)
                OnSessionUnlock();
        }

        base.WndProc(ref m);
        return;
    }    
}

This class exposes three interesting protected members to its derived classes.  The ReceivingLockNotifications property is a flag that indicates that the form is receiving lock and unlock messages.  The most likely reasons this would be set to false is if the form is not running on Windows XP or higher, or the form is disposed.  OnSessionLock and OnSessionUnlock are methods called when the user locks and unlocks the current Windows session.

Control flow through the form is pretty basic.  In the OnHandleCreated method, we check to see if the user is running on XP or higher, and if so, call out to WTSRegisterSessionNotification, requesting notifications only for the current session.  This call needs to be done in OnHandleCreated so that there is an HWND associated with the form, since that HWND is required to register for notifications.

The WndProc is overridden so that we can check to see if we've gotten any WM_WTSESSION_CHANGE messages.  If we have, and their wParam is either lock or unlock, we call the appropriate virtual function.  The rest of the messages just get processed by the default WndProc implementation.

Finally, in the form's Dispose method, we check to see if we ever registered for notifications, and if so unregister.

Using this class is very easy.  Here's a tiny sample that just keeps track of the lock and unlock times in a text box:

using System;
using System.Windows.Forms;

public class NotifyForm : LockNotificationForm
{
    private TextBox textBox = new TextBox();
    
    public static void Main()
    {
        using(NotifyForm notifyForm = new NotifyForm())
            Application.Run(notifyForm);
    }

    public NotifyForm()
    {
        textBox.Top = ClientRectangle.Top;
        textBox.Left = ClientRectangle.Left;
        textBox.Width = ClientRectangle.Width;
        textBox.Height = ClientRectangle.Height;
        textBox.Multiline = true;

        Controls.Add(textBox);
    }
    
    protected override void OnSessionLock()
    {
        textBox.Text = textBox.Text + "Locked at " + DateTime.Now + Environment.NewLine;
    }

    protected override void OnSessionUnlock()
    {
        textBox.Text = textBox.Text + "Unlocked at " + DateTime.Now + Environment.NewLine;
    }
}

Running this code yields results similar to:

Lock/Unlock notification application

Published Tuesday, May 17, 2005 2:05 PM by shawnfa

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Receiving Session Lock and Unlock Notifications

Tuesday, May 17, 2005 5:55 PM by Ohad Israeli
There seems to be a problem with the solution while working under terminal server... if you run multiple sessions of the application only one will catch the notification.

Another area is the message pump...

I had implemented a sense events listener which works great also under terminal server env. and gets notify for events so you don’t have to listen to the message pump.

I'll clean up my code and publish it on my blog soon.

# re: Receiving Session Lock and Unlock Notifications

Tuesday, May 17, 2005 6:33 PM by shawnfa
Hi Ohad,

I've tested this code running multiple instances across multiple terminal server sessions. Each instance is notified of each lock / unlock action that occurs on its own session. Using a Windows Service and SENS will require administrator privileges, which this code does not require.

-Shawn

# Finding out when a machine is locked Part 1

Wednesday, May 18, 2005 4:26 AM by Earning the next skiing holiday

# re: Receiving Session Lock and Unlock Notifications

Wednesday, May 18, 2005 12:39 PM by SeanGep.NET
Great stuff, I was just looking into doing something like this...

# re: Receiving Session Lock and Unlock Notifications

Wednesday, May 18, 2005 12:48 PM by Stupid
Why doesnt C# BCLs ship with a Const library for the windows message constants? Why must we always have to go hunt through the .h files and reimplement the wheel?

# Interesting Links found today

Thursday, May 19, 2005 5:10 PM by the roarty blog
Some links found today - just a good way of making sure I don't lose them:

Receiving Session Lock...

# re: Receiving Session Lock and Unlock Notifications

Friday, May 20, 2005 3:28 PM by Sean Gephardt
Great sample, Shawn!

# Savoir si la session windows est verrouill

Tuesday, June 07, 2005 5:10 AM by VANNESTE Xavier

# Savoir si la session windows est verrouill

Tuesday, June 07, 2005 5:13 AM by VANNESTE Xavier

# Receiving Session Lock and Unlock Notifications

Wednesday, June 15, 2005 12:02 AM by .NET client echoes
This article is pretty useful ...&amp;nbsp;&amp;nbsp; http://blogs.msdn.com/shawnfa/archive/2005/05/17/418891.aspx&amp;nbsp;...

# Little touches to improve Windows desktop applications

Tuesday, February 28, 2006 2:54 PM by Simon Thorneycroft and Jonathan Hodgson
If you are writing rich-client applications for the Windows desktop there are lots of little touches...

# re: Thanks

Saturday, April 22, 2006 10:49 AM by Daniel
Thanks, this was just what I was looking for.

# re: Receiving Session Lock and Unlock Notifications

Tuesday, May 02, 2006 9:21 PM by Richard
This article also explains how to get SENS Events for the same Logon/Logoff events:

http://dotnet.sys-con.com/read/105651.htm

# Very Very Nice

Thursday, May 04, 2006 9:43 PM by Ron
I was looking for somthing to modify for a program I am writing so I can tell if a user had logged back in. This class makes things much easier for me. Thanks so much for this information.

# Screensaver nur bei gesperrtem Computer starten? - Mindfactory AG Community Forum

# Great !

Friday, December 22, 2006 1:18 AM by Alan

Just thanks, it was exactly what I was searching for. Thanks again for posting !

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker