程式化關閉 Windows 看似簡單,實則涉及權限提升、API 版本差異、資料同步等細節。本文整理跨版本相容的關機程式碼,解析 NT 核心特權機制與 9x 系列簡單模式的差異。
BOOL ExitWindowsEx(
UINT uFlags, // 操作類型
DWORD dwReason // 原因代碼
);
常用旗標組合:
EWX_SHUTDOWN | EWX_POWEROFF:關機並切斷電源 (ACPI)EWX_REBOOT:重新開機EWX_LOGOFF:登出當前使用者EWX_FORCE:強制關閉應用程式 (不存檔)EWX_FORCEIFHUNG:僅強制關閉無回應程式關機屬於高危操作,需要 SE_SHUTDOWN_NAME 特權。完整流程:
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
// 1. 開啟進程權杖
OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
// 2. 查詢特權 LUID
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
// 3. 設定特權屬性
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 4. 調整權杖特權
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
// 5. 執行關機
ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF, 0);
無特權模型,直接呼叫 ExitWindowsEx 即可。但建議統一程式碼路徑,執行時檢測 OS 版本決定是否提權。
bool ShutdownSystem(bool bReboot = false, bool bForce = false) {
OSVERSIONINFO osvi = { sizeof(osvi) };
GetVersionEx(&osvi);
UINT flags = bReboot ? EWX_REBOOT : (EWX_SHUTDOWN | EWX_POWEROFF);
if (bForce) flags |= EWX_FORCE;
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
// NT 核心提權流程...
HANDLE hToken; TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
}
// 9x 或提權後統一呼叫
return ExitWindowsEx(flags, SHTDN_REASON_MAJOR_OTHER);
}
ShutdownBlockReasonCreate 註冊阻礙原因,使用者可選擇取消/繼續EWX_HYBRID_SHUTDOWN 核心會話鎖定到磁碟InitiateSystemShutdownEx 支援遠端機器、倒數計時、自訂訊息WM_QUERYENDSESSION / WM_ENDSESSION 讓程式存檔/清理SE_SHUTDOWN_NAMEEWX_LOGOFF 不能與 EWX_SHUTDOWN 合用WM_QUERYENDSESSION 回傳 FALSE 取消關機掌握權限提升與版本差異,即可編寫健壯的跨平台關機工具。