Dealing with Operating System Shutdown, Power Management, or User Session Changes






Dealing with Operating System Shutdown, Power Management, or User Session Changes

Problem

You want to be notified whenever the operating system or a user has initiated an action that requires your application to shut down or be inactive (user logoff, remote session disconnect, system shutdown, hibernate/restore, etc.). This notification will allow you have your application respond gracefully to the changes.

Solution

Use the Microsoft.Win32.SystemEvents class to get notification of operating system, user session change, and power management events. The RegisterForSystemEvents method shown next hooks up the five event handlers necessary to capture these events and would be placed in the initialization section for your code:

	public static void RegisterForSystemEvents( )
	{
	     // Always get the final notification when the event thread is shutting down
	     // so we can unregister.
	     SystemEvents.EventsThreadShutdown +=
	          new EventHandler(OnEventsThreadShutdown);
	     SystemEvents.PowerModeChanged +=
	          new PowerModeChangedEventHandler(OnPowerModeChanged);
	     SystemEvents.SessionSwitch +=
	          new SessionSwitchEventHandler(OnSessionSwitch);
	     SystemEvents.SessionEnding +=
	          new SessionEndingEventHandler(OnSessionEnding);
	     SystemEvents.SessionEnded +=
	          new SessionEndedEventHandler(OnSessionEnded);
	}

The EventsThreadShutdown event notifies you of when the thread that is distributing the events from the SystemEvents class is shutting down so that you can unregister the events on the SystemEvents class if you have not already done so. The PowerModeChanged event triggers when the user suspends or resumes the system from a suspended state. The SessionSwitch event is triggered by a change in the logged-on user. The SessionEnding event is triggered when the user is trying to log off or shut down the system, and the SessionEnded event is triggered when the user is actually logging off or shutting down the system.

The events can be unregistered using the UnregisterFromSystemEvents method. UnregisterFromSystemEvents should be called from the termination code of your Windows Form, user control, or any other class that may come and go, as well as from one other area shown later in the recipe:

	private static void UnregisterFromSystemEvents()
	{
	    SystemEvents.EventsThreadShutdown -=
	        new EventHandler(OnEventsThreadShutdown);
	    SystemEvents.PowerModeChanged -=
	        new PowerModeChangedEventHandler(OnPowerModeChanged);
	    SystemEvents.SessionSwitch -=
	        new SessionSwitchEventHandler(OnSessionSwitch);
	    SystemEvents.SessionEnding -=
	        new SessionEndingEventHandler(OnSessionEnding);
	    SystemEvents.SessionEnded -=
	        new SessionEndedEventHandler(OnSessionEnded);
	}

Since the events exposed by SystemEvents are static, if you are using them in a section of code that could be invoked multiple times (secondary Windows Form, user control, monitoring class, etc.), you must unregister your handlers or you will cause memory leaks in the application.


The SystemEvents handler methods are the individual event handlers for each of the events that have been subscribed to in RegisterForSystemEvents. The first handler to cover is the OnEventsThreadShutdown handler. It is essential that your handlers are unregistered if this event fires, as the notification thread for the SystemEvents class is going away and the class may be gone before your application is. If you haven't unregistered before that point, you will cause memory leaks, so add a call to UnregisterFromSystemEvents into this handler as shown here:

	private static void OnEventsThreadShutdown(object sender, EventArgs e)
	{
	    Debug.WriteLine("System event thread is shutting down, no more notifications.");
	    // Unregister all our events as the notification thread is going away.
	    UnregisterFromSystemEvents();
	}

The next handler to explore is the OnPowerModeChanged method. This handler can report the type of power management event through the Mode property of the PowerModeEventChangedArgs parameter. The Mode property has the PowerMode enumeration type and specifies the event type through the enumeration value contained therein.

	private static void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
	{
	    // Power mode is changing.
	    switch (e.Mode)
	    {
	        case PowerModes.Resume:
	            Debug.WriteLine("PowerMode: OS is resuming from suspended state");
	            break;
	        case PowerModes.StatusChange:
	            Debug.WriteLine("PowerMode: There was a change relating to the power" +
	                " supply (weak battery, unplug, etc..)");
	            break;
	        case PowerModes.Suspend:
	            Debug.WriteLine("PowerMode: OS is about to be suspended");
	            break;
	    }
	}

The next three handlers all deal with operating system session states. They are OnSessionSwitch, OnSessionEnding, and OnSessionEnded. Handling all three of these events covers all of the operating system session state transitions that your application may need to worry about. In OnSessionEnding, there is a SessionEndingEventArgs parameter, which has a Cancel member. This Cancel member allows you to request that the session not end if set to false. Code for the three handlers is shown in Figure.

OnSessionSwitch, OnSessionEnding, and OnSessionEnded handlers

private static void OnSessionSwitch(object sender, SessionSwitchEventArgs e)
{
    // Check reason.
    switch (e.Reason)
    {
        case SessionSwitchReason.ConsoleConnect:
            Debug.WriteLine("Session connected from the console");
            break;
        case SessionSwitchReason.ConsoleDisconnect:
            Debug.WriteLine("Session disconnected from the console");
            break;
        case SessionSwitchReason.RemoteConnect:
            Debug.WriteLine("Remote session connected");
            break;
        case SessionSwitchReason.RemoteDisconnect:
            Debug.WriteLine("Remote session disconnected");
            break;
        case SessionSwitchReason.SessionLock:
            Debug.WriteLine("Session has been locked");
            break;
        case SessionSwitchReason.SessionLogoff:
            Debug.WriteLine("User was logged off from a session");
            break;
        case SessionSwitchReason.SessionLogon:
            Debug.WriteLine("User has logged on to a session");
            break;
        case SessionSwitchReason.SessionRemoteControl:
            Debug.WriteLine("Session changed to or from remote status");
            break;
        case SessionSwitchReason.SessionUnlock:
            Debug.WriteLine("Session has been unlocked");
            break;
    }
}

private static void OnSessionEnding(object sender, SessionEndingEventArgs e)
{
    // True to cancel the user request to end the session, false otherwise
    e.Cancel = false;
    // Check reason.
    switch(e.Reason)
    {
        case SessionEndReasons.Logoff:
            Debug.WriteLine("Session ending as the user is logging off");
            break;
        case SessionEndReasons.SystemShutdown:
            Debug.WriteLine("Session ending as the OS is shutting down");
            break;
    }
}

private static void OnSessionEnded(object sender, SessionEndedEventArgs e)
{
    switch (e.Reason)
    {
        case SessionEndReasons.Logoff:
            Debug.WriteLine("Session ended as the user is logging off");
            break;
        case SessionEndReasons.SystemShutdown:
            Debug.WriteLine("Session ended as the OS is shutting down");
            break;
    }
}

Discussion

The .NET Framework provides many opportunities to get feedback from the system when there are changes due to either user or system interactions. The SystemEvents class exposes more events than just the ones used in this recipe. For a full listing, see

The SystemEvents events

Value

Description

DisplaySettingsChanged

User changed display settings.

DisplaySettingsChanging

Display settings are changing.

EventsThreadShutdown

Thread listening for system events is terminating.

InstalledFontsChanged

User added or removed fonts.

PaletteChanged

User switched to an application with a different palette.

PowerModeChanged

User suspended or resumed the system.

SessionEnded

User shut down the system or logged off.

SessionEnding

User is attempting to shut down the system or log off.

SessionSwitch

The currently logged-in user changed.

TimeChanged

User changed system time.

TimerElapsed

A Windows timer interval expired.

UserPreferenceChanged

User changed a preference in the system.

UserPreferenceChanging

User is trying to change a preference in the system.


Keep in mind that these are system events. Therefore, the amount of work done in the handlers should be kept to a minimum so the system can move on to the next task.


The notifications from SystemEvents come on a dedicated thread for raising these events. In a Windows Forms application, you will need to get back on to the correct user interface thread before updating a UI with any of this information, using one of the various methods for doing so (Control.BeginInvoke, Control.Invoke, BackgroundWorker).

See Also

See the "SystemEvents Class," "PowerModeChangedEventArgs Class," "SessionEndedEventArgs Class," "SessionEndingEventArgs Class," "SessionSwitchEventArgs Class," "TimerElapsedEventArgs Class," "UserPreferenceChangingEventArgs Class," and "UserPreferenceChangedEventArgs Class" topics in the MSDN documentation.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows