首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC++

VC++ 實現 Windows 2000/XP 系統休眠/待機功能

     編輯:VC++

核心難點:NT 內核系統需提權調用電源管理 API

為什麼 9x 簡單而 NT 復雜?

  • Win9x:ExitWindowsEx/SetSystemPowerState 直接調用即可,無權限隔離。
  • Win2000/XP/2003/Vista+:基於 NT 內核,實施特權檢查。調用電源管理 API 需進程擁有 SE_SHUTDOWN_NAME 特權。

完整實現代碼


#include 
#include 

#define RTN_ERROR 13

// 錯誤報告輔助
void ReportError(LPCTSTR szAPI, DWORD dwErr) {
    LPTSTR pMsg = NULL;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, dwErr, LANG_NEUTRAL, (LPTSTR)&pMsg, 0, NULL);
    _tprintf(_T("%s 失敗 (錯誤碼: %lu): %s\n"), szAPI, dwErr, pMsg ? pMsg : _T("未知"));
    LocalFree(pMsg);
}

// 核心函數:設置系統電源狀態
// bHibernate: TRUE=休眠, FALSE=待機
// bForce: TRUE=強制(不通知應用), FALSE=正常
BOOL SetPowerState(BOOL bHibernate, BOOL bForce) {
    HANDLE hToken = NULL;
    TOKEN_PRIVILEGES tp = {0};
    LUID luid;

    // 1. 打開當前進程令牌
    if (!OpenProcessToken(GetCurrentProcess(), 
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        ReportError(_T("OpenProcessToken"), GetLastError());
        return FALSE;
    }

    // 2. 查找關機特權 LUID
    if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid)) {
        ReportError(_T("LookupPrivilegeValue"), GetLastError());
        CloseHandle(hToken);
        return FALSE;
    }

    // 3. 構造特權結構並啟用
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
        ReportError(_T("AdjustTokenPrivileges"), GetLastError());
        CloseHandle(hToken);
        return FALSE;
    }
    // 注意:AdjustTokenPrivileges 成功不代表特權生效,需檢查 GetLastError() == ERROR_SUCCESS
    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
        _tprintf(_T("當前用戶無關機特權,請以管理員身份運行或組策略授予。\n"));
        CloseHandle(hToken);
        return FALSE;
    }

    // 4. 調用電源管理 API
    if (!SetSystemPowerState(bHibernate, bForce)) {
        ReportError(_T("SetSystemPowerState"), GetLastError());
        CloseHandle(hToken);
        return FALSE;
    }

    CloseHandle(hToken);
    return TRUE;
}

int _tmain() {
    _tprintf(_T("1. 待機 (Sleep)\n2. 休眠\n選擇: "));
    int choice; _tscanf(_T("%d"), &choice);
    
    if (choice == 1) SetPowerState(FALSE, FALSE);
    else if (choice == 2) SetPowerState(TRUE, FALSE);
    else _tprintf(_T("無效選項\n"));
    
    return 0;
}

關鍵點解析

  1. 特權提升流程:OpenProcessToken → LookupPrivilegeValue → AdjustTokenPrivileges。這是所有需管理員權限操作的標準模式(關機、修改系統時間、加載驅動等)。
  2. SE_SHUTDOWN_NAME:對應「關閉系統」用戶權限。組策略:本地策略 → 用戶權限分配 → 關閉系統。
  3. SetSystemPowerState 參數:
    • bHibernate:TRUE = 休眠 (S4,內存寫入硬盤斷電);FALSE = 待機 (S3,內存供電)。
    • bForce:TRUE 強制掛起,應用無法攔截 WM_POWERBROADCAST/PBT_APMQUERYSUSPEND;FALSE 允許應用阻止(如燒錄軟體)。
  4. 前置條件:控制面板 → 電源選項 → 休眠/待機 必須啟用,且硬體支持 ACPI。

常見失敗原因排查

現象原因解決
返回 FALSE,錯誤碼 1300 (ERROR_NOT_ALL_ASSIGNED)用戶無 SE_SHUTDOWN_NAME 特權管理員運行 / 組策略添加用戶到「關閉系統」
調用成功但系統不休眠未啟用休眠 / 驅動不支持 / 內存不足無法寫入休眠文件電源選項啟用休眠 / 更新驅動 / 增加頁面文件
立即喚醒USB 設備/網卡喚醒 / 定時任務設備管理器禁用「允許喚醒」 / powercfg -devicequery wake_armed

擴展:關機/重啟/登出統一封裝


BOOL SystemPowerAction(UINT uAction) {
    // uAction: EWX_LOGOFF, EWX_SHUTDOWN, EWX_REBOOT, EWX_POWEROFF
    // 需同樣提權 SE_SHUTDOWN_NAME
    return ExitWindowsEx(uAction | EWX_FORCE, SHTDN_REASON_MAJOR_APPLICATION);
}

掌握特權提升模式,是開發系統級工具(服務管理、驅動加載、電源控制)的必備基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved