 .NET 1.1Exiting Windows and Logging Off Programmatically
If you develop software that updates key Windows configuration settings, you may require that the system be restarted or that the users logs out and performs a new login process. Using Windows API functions, this can be controlled programmatically in C#.
ExitWindowsEx API Function
The ExitWindowsEx application programming interface (API) function allows a message to be sent to all executing processes for the current user, requesting that they terminate to allow the computer to be shut down, restarted or in preparation for the current user to be logged off. The function is available in Windows 2000 and later operating systems.
The API call is used to terminate all of the current user's processes. When the user executing the function is the interactive user, this can also cause the system to automatically shut down or restart. When the command is called from a service that is running under a different user's credentials or within a Terminal Services session, the function can successfully stop all of the current processes without initiating a shutdown of the physical server. NB: In this situation if a shutdown is required the InitiateSystemShutdownEx API can be used instead.
Creating the Sample Program
In this article a sample program will be created that demonstrates how to programmatically power off or restart the system. This program will then be modified to show the other uses of the API function. To begin, create a new Windows application named "ShutdownSample". Add three buttons to the initial form and set the properties for the buttons as follows:
| Id | Text |
|---|
| ShutdownButton | Shut Down | | RestartButton | Restart | | LogoffButton | Log Off |
You may also want to change the Id of the form and the text that appears in its caption. The end result should look similar to the form shown below.

Referencing the ExitWindowsEx API Function
The .NET framework does not natively provide any means of shutting down the system. Instead, you must use a Windows API function for this task. The first thing that we need to do is to reference the InteropServices namespace so that we can access the Windows API functions. To do this, add a new using statement at the start of the code for the form.
using System.Runtime.InteropServices;
Once the reference to the InteropServices namespace has been added, we can declare the API function as a static function. Ensure that the following declaration appears within the form's class code block but not within any method or property declaration.
[DllImport("user32.dll", SetLastError = true)]
static extern int ExitWindowsEx(uint uFlags, uint dwReason);
Function Flags
The ExitWindowsEx function requires two parameters to be specified. The first parameter, uFlags, determines the action that should be taken when the function is called. The integer value to use is determined by combining one or more flag values. To make the code easier to read, we will define a new enumeration containing the key values for our purposes.
Add the following enumeration code within the form's code block.
enum ExitFlags
{
Logoff = 0,
Shutdown = 1,
Reboot = 2,
Force = 4,
PowerOff = 8,
ForceIfHung = 16
}
Shutdown Reason Codes
The second parameter of the ExitWindowsEx function is used to define the reason that the system was shut down. There are eight standard major reasons for Windows to be exited or restarted. These can each be modified using minor reason values. There are several tens of reason codes available that are described in Microsoft's System Shutdown Reason Codes MSDN article. For the simple demonstration of the codes, we will define just a few of the major reasons in a second enumeration.
enum Reason : uint
{
ApplicationIssue = 0x00040000,
HardwareIssue = 0x00010000,
SoftwareIssue = 0x00030000,
PlannedShutdown = 0x80000000
}
Elevating Privileges
All programs execute with a set of privileges that enable and disable Windows functionality. For security purposes, the privileges provided to a program should be the minimum necessary to ensure correct execution. For this reason, the ability to shut down Windows is not provided as standard to .NET software. The privilege must be requested before the shut down command is available.
Elevating the program's privileges to include the required shut down rights uses a series of API calls. The following four functions are used:
- GetCurrentProcess. This function gets a handle that represents the current process. The handle is used to identify the process when elevating the privileges.
- OpenProcessToken. This function retrieves the access token for a process. When used with the current process handle, the access token holds the privilege information for the executing program.
- LookupPrivilegeValue. This function is used to perform a lookup of the locally unique identifier (LUID) for a named privilege. This allows the LUID for the shutdown privilege to be found in readiness for elevating the privileges of the process.
- AdjustTokenPrivileges. Once the process handle, access token and privilege LUID are identified, this function is used to change the program's privileges and assign the shut down rights.
Adding a Privilege Elevation Method
We will now add a new method to the sample program that assigns the privilege to the current process before attempting any shutdown procedure. Before we can add this method, the four API calls must be declared. These API calls also use some constant values and a structure. Declare these as follows within the form's class code block.
const int PrivilegeEnabled = 0x00000002;
const int TokenQuery = 0x00000008;
const int AdjustPrivileges = 0x00000020;
const string ShutdownPrivilege = "SeShutdownPrivilege";
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokenPrivileges
{
public int PrivilegeCount;
public long Luid;
public int Attributes;
}
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int OpenProcessToken(
IntPtr processHandle,
int desiredAccess,
ref IntPtr tokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int LookupPrivilegeValue(
string systemName, string name, ref long luid);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int AdjustTokenPrivileges(
IntPtr tokenHandle, bool disableAllPrivileges,
ref TokenPrivileges newState,
int bufferLength,
IntPtr previousState,
IntPtr length);
With these declarations made, the new method can be added to the code. This method uses the four API functions in turn to identify the current process, retrieve the current privileges, identify the shutdown privilege and grant it to the user. Add the following method to the form's code:
private void ElevatePrivileges()
{
IntPtr currentProcess = GetCurrentProcess();
IntPtr tokenHandle = IntPtr.Zero;
int result = OpenProcessToken(
currentProcess,
AdjustPrivileges | TokenQuery,
ref tokenHandle);
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
TokenPrivileges tokenPrivileges;
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Luid = 0;
tokenPrivileges.Attributes = PrivilegeEnabled;
result = LookupPrivilegeValue(
null,
ShutdownPrivilege,
ref tokenPrivileges.Luid);
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
result = AdjustTokenPrivileges(
tokenHandle,
false,
ref tokenPrivileges,
0, IntPtr.Zero,
IntPtr.Zero);
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
Exiting Windows
With the API referenced, the flag and reason code enumerations available and a method that elevates the program's privileges to permit shutdowns, we can now add functionality to the form's buttons to demonstrate the use of the ExitWindowsEx method.
The first piece of code to add to the program will cause Windows to power off when the Shut Down button is clicked. To cause Windows to exit completely, the Shutdown flag is used. On Windows XP service pack 1 operating systems and later this causes Windows to terminate and the computer's power to be switched off (if supported by the machine's hardware). However, on earlier operating systems the Shutdown flag alone will exit Windows but leave the machine powered on. This can be overcome by combining the Shutdown and PowerOff flags.
When the ExitWindowsEx function is called, it returns an integer value indicating success or failure. If the call fails, the return value is zero and the error details are stored. The error can be retrieved using the GetLastWin32Error method of the Marshal class. Although this method returns an integer, the details of the exception can be seen by throwing a new Win32Exception, passing the error number as a parameter.
To add the shutdown and error detection code to the Shut Down button, add the following code to the button's Click event. This sample combines two reason codes to indicate to the operating system that the system was powered off due to a hardware issue but that this was planned.
private void ShutdownButton_Click(object sender, EventArgs e)
{
ElevatePrivileges();
int result = ExitWindowsEx(
(uint)(ExitFlags.Shutdown | ExitFlags.PowerOff),
(uint)(Reason.HardwareIssue | Reason.PlannedShutdown));
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
You can test the program by executing it and pressing the Shut Down button. Be sure to save the program and any other files that you are working on first.
Forcing a Shutdown
The shutdown procedure described above sends a message to all of the current user's processes requesting that they close. This is similar to closing Windows via the Start menu. Each program that is running will close normally, giving the user the option to save any open work where appropriate. If any of the running software fails to close within a timeout period, the user is given the option to terminate the programs forcibly or cancel the shutdown procedure.
In some emergency cases, such as when programs hang, you may want to force the system to close all of the active processes. By including the Force flag, all processes are forced to close and the user is not given the option to cancel the process. The ForceIfHung flag can also be used to force applications to terminate if they do not respond within the timeout period. Either option should be used with care as they can close applications without giving the user the option to save their work first.
To create a forced shutdown, adjust the ShutdownButton_Click event as follows:
private void ShutdownButton_Click(object sender, EventArgs e)
{
ElevatePrivileges();
int result = ExitWindowsEx(
(uint)(ExitFlags.Shutdown | ExitFlags.PowerOff | ExitFlags.Force),
(uint)(Reason.HardwareIssue | Reason.PlannedShutdown));
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
Restarting Windows
If your software has made changes to the configuration of Windows, you may require the operating system to be restarted rather than simply powered down. This is achieved by modifying the ExitWindowsEx flags parameter so that it uses the Reboot flag, rather than the various Shutdown flags. The force flags may also be combined with the Reboot flag.
To add the restart code to the sample application, create a Click event for the restart button as follows:
private void RestartButton_Click(object sender, EventArgs e)
{
ElevatePrivileges();
int result = ExitWindowsEx(
(uint)(ExitFlags.Reboot),
(uint)(Reason.SoftwareIssue | Reason.PlannedShutdown));
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
Logging Off Windows
The last button in the sample application will be used to simply log the user out of Windows. This is useful for minor configuration changes where a full reboot is not necessary. Again, the flags in the ExitWindowsEx function are the only change required.
To complete the sample program, add a Click event for the log off button as follows:
private void LogoffButton_Click(object sender, EventArgs e)
{
ElevatePrivileges();
int result = ExitWindowsEx(
(uint)(ExitFlags.Logoff),
(uint)(Reason.ApplicationIssue | Reason.PlannedShutdown));
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
|