Windows 關機需要 SE_SHUTDOWN_PRIVILEGE 特權。流程:
OpenProcessToken)LookupPrivilegeValue)AdjustTokenPrivileges)ExitWindowsEx 執行關機/重開機/登出using System;
using System.Runtime.InteropServices;
public static class WindowsShutdown
{
// --- 常數 ---
private const int SE_PRIVILEGE_ENABLED = 0x00000002;
private const int TOKEN_QUERY = 0x00000008;
private const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
private const int EWX_LOGOFF = 0x00;
private const int EWX_SHUTDOWN = 0x01;
private const int EWX_REBOOT = 0x02;
private const int EWX_FORCE = 0x04;
private const int EWX_POWEROFF = 0x08;
private const int EWX_FORCEIFHUNG = 0x10;
// --- 結構 ---
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
public long Luid;
public int Attributes;
}
// --- P/Invoke 宣告 ---
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref long lpLuid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool ExitWindowsEx(int uFlags, int dwReason);
// --- 公開方法 ---
public static void Shutdown(bool force = false) => ExitWin(EWX_SHUTDOWN | (force ? EWX_FORCE : 0));
public static void Reboot(bool force = false) => ExitWin(EWX_REBOOT | (force ? EWX_FORCE : 0));
public static void Logoff(bool force = false) => ExitWin(EWX_LOGOFF | (force ? EWX_FORCE : 0));
public static void PowerOff(bool force = false) => ExitWin(EWX_POWEROFF | (force ? EWX_FORCE : 0));
private static void ExitWin(int flags)
{
IntPtr hToken = IntPtr.Zero;
var tp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Attributes = SE_PRIVILEGE_ENABLED };
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref hToken))
ThrowWin32();
if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid))
ThrowWin32();
if (!AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
ThrowWin32();
if (!ExitWindowsEx(flags, 0))
ThrowWin32();
}
private static void ThrowWin32() => throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
// 一般關機
WindowsShutdown.Shutdown();
// 強制重開機 (不等待應用程式關閉)
WindowsShutdown.Reboot(force: true);
// 登出目前使用者
WindowsShutdown.Logoff();
AdjustTokenPrivileges 會失敗 (錯誤 1300: NOT_ALL_ASSIGNED)。System.Diagnostics.Process.Start("shutdown", "/s /t 0") 簡單場景,但無法精確控制特權流程。透過 P/Invoke 完整實作 Windows 關機特權啟用流程,提供 Shutdown/Reboot/Logoff/PowerOff 靜態方法,適合需精確控制關機行為的系統管理工具或嵌入式應用。