Prevent Windows shutdown or session ending in .NET

When the user logs off or when he stops his machine, Windows quits all open applications. Some applications want to execute code before ending in order not to lose the current state (a document being written for example).

For this Windows sends a message to all applications:

  • WM_QUERYENDSESSION when closing the session
  • WM_ENDSESSION when shutting down the machine

If the application does not process these messages, Windows closes the farm. However, it is possible to intercept these messages and to block the machine shutdown or logoff. In this case, this window is displayed:

Windows provides 3 methods:

The final method determines the order in which Windows sends the WM_ENDSESSION and WM_QUERYENDSESSION messages to the applications. This is used in the case where an application A depends on an application B. The application A wishes to be informed before the application B that the machine is stopping to potentially block this stop. Thus application B remains alive as application A blocks the shutdown.

The priority is defined with an integer between 0 and 0x4FF. 0x4FF is the highest priority (called first), 0 is the lowest priority (last called). By default applications have priority 280.

Here is a small code example to show how to use these methods:

public partial class MainForm : Form
{
    public const int WM_QUERYENDSESSION = 0x0011;
    public const int WM_ENDSESSION = 0x0016;
    public const uint SHUTDOWN_NORETRY = 0x00000001;

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string reason);
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);
    [DllImport("kernel32.dll")]
    static extern bool SetProcessShutdownParameters(uint dwLevel, uint dwFlags);

    public MainForm()
    {
        InitializeComponent();
        // Define the priority of the application (0x3FF = The higher priority)
        SetProcessShutdownParameters(0x3FF, SHUTDOWN_NORETRY);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_QUERYENDSESSION || m.Msg == WM_ENDSESSION)
        {
            // Prevent windows shutdown
            ShutdownBlockReasonCreate(this.Handle, "I want to live!");
            ThreadPool.QueueUserWorkItem(o =>
            {
                // Simulate some work
                Thread.Sleep(5000);
                this.BeginInvoke((Action)(() =>
                {
                    // This method must be called on the same thread as the one that have create the Handle, so use BeginInvoke
                    ShutdownBlockReasonCreate(this.Handle, "Now I must die!");

                    // Allow Windows to shutdown
                    ShutdownBlockReasonDestroy(this.Handle);
                }));
            });

            return;
        }

        base.WndProc(ref m);
    }
}
Follow me:
Enjoy this blog? Buy Me A Coffee Donate with PayPal

Leave a reply