When using ExitWindowsEx, logoff works, but shutdown and restart do not

5

When I call logoff, it works. But shutdown and restart don't work. Everything looks OK. I looked at other examples on SO and else where and the code looks pretty uniform across most places. So I'm thinking it might be something other than the code.

I'm running as admin and I tried it without the force flag.

public void ShutdownComputer(ShutdownType type, bool force)
    {
        switch (type)
        {
            case ShutdownType.Shutdown:
                ExitWindowsEx(ExitWindows.ShutDown | (force ? ExitWindows.Force : ExitWindows.ForceIfHung), ShutdownReason.MajorOther | ShutdownReason.MinorOther | ShutdownReason.FlagPlanned); 
                break;
            case ShutdownType.Restart:
                ExitWindowsEx(ExitWindows.Reboot | (force ? ExitWindows.Force : ExitWindows.ForceIfHung), ShutdownReason.MajorOther | ShutdownReason.MinorOther | ShutdownReason.FlagPlanned); 
                break;
            case ShutdownType.Logoff:
                ExitWindowsEx(ExitWindows.LogOff | (force ? ExitWindows.Force : ExitWindows.ForceIfHung), ShutdownReason.MajorOther | ShutdownReason.MinorOther | ShutdownReason.FlagPlanned); 
                break;
        }
    }

[DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool ExitWindowsEx(ExitWindows uFlags, ShutdownReason dwReason);

    [Flags]
    public enum ExitWindows : uint
    {
        // ONE of the following five:
        LogOff = 0x00,
        ShutDown = 0x01,
        Reboot = 0x02,
        PowerOff = 0x08,
        RestartApps = 0x40,
        // plus AT MOST ONE of the following two:
        Force = 0x04,
        ForceIfHung = 0x10,
    }
    [Flags]
    enum ShutdownReason : uint
    {
        MajorApplication = 0x00040000,
        MajorHardware = 0x00010000,
        MajorLegacyApi = 0x00070000,
        MajorOperatingSystem = 0x00020000,
        MajorOther = 0x00000000,
        MajorPower = 0x00060000,
        MajorSoftware = 0x00030000,
        MajorSystem = 0x00050000,

        MinorBlueScreen = 0x0000000F,
        MinorCordUnplugged = 0x0000000b,
        MinorDisk = 0x00000007,
        MinorEnvironment = 0x0000000c,
        MinorHardwareDriver = 0x0000000d,
        MinorHotfix = 0x00000011,
        MinorHung = 0x00000005,
        MinorInstallation = 0x00000002,
        MinorMaintenance = 0x00000001,
        MinorMMC = 0x00000019,
        MinorNetworkConnectivity = 0x00000014,
        MinorNetworkCard = 0x00000009,
        MinorOther = 0x00000000,
        MinorOtherDriver = 0x0000000e,
        MinorPowerSupply = 0x0000000a,
        MinorProcessor = 0x00000008,
        MinorReconfig = 0x00000004,
        MinorSecurity = 0x00000013,
        MinorSecurityFix = 0x00000012,
        MinorSecurityFixUninstall = 0x00000018,
        MinorServicePack = 0x00000010,
        MinorServicePackUninstall = 0x00000016,
        MinorTermSrv = 0x00000020,
        MinorUnstable = 0x00000006,
        MinorUpgrade = 0x00000003,
        MinorWMI = 0x00000015,

        FlagUserDefined = 0x40000000,
        FlagPlanned = 0x80000000
    }
c#
winforms
asked on Stack Overflow Jul 13, 2014 by ernest

1 Answer

10

In order to be able to shutdown the computer you have to enable the SeShutdown privilege. Once this is done you can call ExitWindowEx

public static class PowerUtilities
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern int ExitWindowsEx(ExitWindows uFlags, ShutdownReason dwReason);

    public static bool ExitWindows(ExitWindows exitWindows, ShutdownReason reason, bool ajustToken)
    {
        if (ajustToken && !TokenAdjuster.EnablePrivilege("SeShutdownPrivilege", true))
        {
            return false;
        }


        return ExitWindowsEx(exitWindows, reason) != 0;
    }
}


[Flags]
public enum ExitWindows : uint
{
    // ONE of the following:
    LogOff = 0x00,
    ShutDown = 0x01,
    Reboot = 0x02,
    PowerOff = 0x08,
    RestartApps = 0x40,
    // plus AT MOST ONE of the following two:
    Force = 0x04,
    ForceIfHung = 0x10,
}

[Flags]
public enum ShutdownReason : uint
{
    None = 0,

    MajorApplication = 0x00040000,
    MajorHardware = 0x00010000,
    MajorLegacyApi = 0x00070000,
    MajorOperatingSystem = 0x00020000,
    MajorOther = 0x00000000,
    MajorPower = 0x00060000,
    MajorSoftware = 0x00030000,
    MajorSystem = 0x00050000,

    MinorBlueScreen = 0x0000000F,
    MinorCordUnplugged = 0x0000000b,
    MinorDisk = 0x00000007,
    MinorEnvironment = 0x0000000c,
    MinorHardwareDriver = 0x0000000d,
    MinorHotfix = 0x00000011,
    MinorHung = 0x00000005,
    MinorInstallation = 0x00000002,
    MinorMaintenance = 0x00000001,
    MinorMMC = 0x00000019,
    MinorNetworkConnectivity = 0x00000014,
    MinorNetworkCard = 0x00000009,
    MinorOther = 0x00000000,
    MinorOtherDriver = 0x0000000e,
    MinorPowerSupply = 0x0000000a,
    MinorProcessor = 0x00000008,
    MinorReconfig = 0x00000004,
    MinorSecurity = 0x00000013,
    MinorSecurityFix = 0x00000012,
    MinorSecurityFixUninstall = 0x00000018,
    MinorServicePack = 0x00000010,
    MinorServicePackUninstall = 0x00000016,
    MinorTermSrv = 0x00000020,
    MinorUnstable = 0x00000006,
    MinorUpgrade = 0x00000003,
    MinorWMI = 0x00000015,

    FlagUserDefined = 0x40000000,
    FlagPlanned = 0x80000000
}

public sealed class TokenAdjuster
{
    // PInvoke stuff required to set/enable security privileges
    private const int SE_PRIVILEGE_ENABLED = 0x00000002;
    private const int TOKEN_ADJUST_PRIVILEGES = 0X00000020;
    private const int TOKEN_QUERY = 0X00000008;
    private const int TOKEN_ALL_ACCESS = 0X001f01ff;
    private const int PROCESS_QUERY_INFORMATION = 0X00000400;

    [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity]
    private static extern int OpenProcessToken(
        IntPtr ProcessHandle, // handle to process
        int DesiredAccess, // desired access to process
        ref IntPtr TokenHandle // handle to open access token
        );

    [DllImport("kernel32", SetLastError = true),
     SuppressUnmanagedCodeSecurity]
    private static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int AdjustTokenPrivileges(
        IntPtr TokenHandle,
        int DisableAllPrivileges,
        IntPtr NewState,
        int BufferLength,
        IntPtr PreviousState,
        ref int ReturnLength);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool LookupPrivilegeValue(
        string lpSystemName,
        string lpName,
        ref LUID lpLuid);

    public static bool EnablePrivilege(string lpszPrivilege, bool bEnablePrivilege)
    {
        bool retval = false;
        int ltkpOld = 0;
        IntPtr hToken = IntPtr.Zero;
        TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
        tkp.Privileges = new int[3];
        TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES();
        tkpOld.Privileges = new int[3];
        LUID tLUID = new LUID();
        tkp.PrivilegeCount = 1;
        if (bEnablePrivilege)
            tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
        else
            tkp.Privileges[2] = 0;
        if (LookupPrivilegeValue(null, lpszPrivilege, ref tLUID))
        {
            Process proc = Process.GetCurrentProcess();
            if (proc.Handle != IntPtr.Zero)
            {
                if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                    ref hToken) != 0)
                {
                    tkp.PrivilegeCount = 1;
                    tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
                    tkp.Privileges[1] = tLUID.HighPart;
                    tkp.Privileges[0] = tLUID.LowPart;
                    const int bufLength = 256;
                    IntPtr tu = Marshal.AllocHGlobal(bufLength);
                    Marshal.StructureToPtr(tkp, tu, true);
                    if (AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref ltkpOld) != 0)
                    {
                        // successful AdjustTokenPrivileges doesn't mean privilege could be changed
                        if (Marshal.GetLastWin32Error() == 0)
                        {
                            retval = true; // Token changed
                        }
                    }
                    TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(tu, typeof(TOKEN_PRIVILEGES));
                    Marshal.FreeHGlobal(tu);
                }
            }
        }
        if (hToken != IntPtr.Zero)
        {
            CloseHandle(hToken);
        }
        return retval;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct LUID
    {
        internal int LowPart;
        internal int HighPart;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LUID_AND_ATTRIBUTES
    {
        private LUID Luid;
        private int Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct TOKEN_PRIVILEGES
    {
        internal int PrivilegeCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        internal int[] Privileges;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct _PRIVILEGE_SET
    {
        private int PrivilegeCount;
        private int Control;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // ANYSIZE_ARRAY = 1
        private LUID_AND_ATTRIBUTES[] Privileges;
    }
}
answered on Stack Overflow Jul 13, 2014 by meziantou

User contributions licensed under CC BY-SA 3.0