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 sessionWM_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:
- ShutdownBlockReasonCreate: Indicates that the system cannot be shut down and sets a reason string to be displayed to the user if system shutdown is initiated ("I want to live" in the screenshot)
- ShutdownBlockReasonDestroy: Indicates that the system can be shut down
- SetProcessShutdownParameters: This function sets a shutdown order for a process relative to the other processes in the system
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);
}
}
Do you have a question or a suggestion about this post? Contact me!