在 Windows 98 時代,呼叫 SetSystemPowerState() 即可讓系統進入休眠,但自 Windows 2000/XP 起,作業系統引入嚴格的使用者權限模型,若未擁有 SE_SHUTDOWN_NAME 特權,該 API 會直接失敗。本文示範如何在 VC++ 中透過調整程序權杖權限,安全地實現系統休眠。
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid)PrivilegeCount = 1,Attributes = SE_PRIVILEGE_ENABLEDAdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)#define RTN_ERROR 13
void PERR(LPCTSTR szAPI, DWORD dwLastError) {
LPTSTR MessageBuffer = nullptr;
DWORD dwBufferLength = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwLastError, LANG_NEUTRAL,
reinterpret_cast(&MessageBuffer), 0, NULL);
if (dwBufferLength) {
fprintf(stderr, _T("%s error! (rc=%lu) "), szAPI, dwLastError);
DWORD dwBytesWritten;
WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer, dwBufferLength, &dwBytesWritten, NULL);
LocalFree(MessageBuffer);
}
}
INT SetPower() {
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
LPCTSTR MachineName = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
PERR(_T("OpenProcessToken"), GetLastError());
return RTN_ERROR;
}
if (!LookupPrivilegeValue(MachineName, SE_SHUTDOWN_NAME, &luid)) {
PERR(_T("LookupPrivilegeValue"), GetLastError());
CloseHandle(hToken);
return RTN_ERROR;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
PERR(_T("AdjustTokenPrivileges"), GetLastError());
CloseHandle(hToken);
return RTN_ERROR;
}
CloseHandle(hToken);
// 觸發休眠:FALSE=休眠(非強制), TRUE=關閉電源前不強制應用程式關閉
if (!SetSystemPowerState(FALSE, TRUE)) {
PERR(_T("SetSystemPowerState"), GetLastError());
return RTN_ERROR;
}
return 0;
}
SetSystemPowerState 會失敗。PERR 使用 FormatMessage 取得系統錯誤描述,方便除錯。同樣的權限提升模式適用於 ExitWindowsEx(登出/重開機/關機)、InitiateSystemShutdown 等需要 SE_SHUTDOWN_NAME 特權的 API。掌握此模式,即可在 NT 核心系統上安全執行電源管理操作。