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

禁止使用者關閉 Windows 系統:攔截 WM_QUERYENDSESSION 實作指南

     編輯:VC 教學

核心機制:系統關機協商流程

當使用者啟動關機/重開機/登出時,Windows 會向所有最上層視窗發送 `WM_QUERYENDSESSION` 訊息。應用程式可透過回傳值決定是否允許:

  • TRUE (1):同意關機,系統繼續發送 `WM_ENDSESSION`
  • FALSE (0):拒絕關機,**系統中止關機流程**

此機制設計用於:資料未儲存提醒、關鍵作業進行中 (備份、燒錄、資料庫交易)、自動更新安裝階段等場景。

C++ Builder 完整實作

1. 訊息映射宣告 (Header)


class TForm1 : public TForm {
__published:
    TCheckBox* CheckBox1; // 控制是否允許關機
private:
    void __fastcall OnWMQueryEndSession(TMessage& Message);
protected:
    BEGIN_MESSAGE_MAP
        VCL_MESSAGE_HANDLER(WM_QUERYENDSESSION, TMessage, OnWMQueryEndSession)
    END_MESSAGE_MAP(TForm)
public:
    __fastcall TForm1(TComponent* Owner);
};

2. 訊息處理實作 (Source)


void __fastcall TForm1::OnWMQueryEndSession(TMessage& Message) {
    // 使用者勾選核取方塊表示允許關機
    if (CheckBox1->Checked) {
        Message.Result = 1; // 同意關機
    } else {
        Message.Result = 0; // 拒絕關機
        // 可選:顯示提示訊息 (需非模態,避免阻塞系統關機逾時)
        // Application->MessageBox("關鍵作業進行中,暫不允許關機!", "系統鎖定", MB_OK | MB_ICONWARNING);
    }
}

進階應用模式

1. 布林旗標控制 (無 UI 介入)


class TCriticalOperationGuard {
    static bool s_operationInProgress;
public:
    static void Begin() { s_operationInProgress = true; }
    static void End() { s_operationInProgress = false; }
    static bool IsBusy() { return s_operationInProgress; }
};

// 在 WM_QUERYENDSESSION 中
void __fastcall TForm1::OnWMQueryEndSession(TMessage& Message) {
    Message.Result = TCriticalOperationGuard::IsBusy() ? 0 : 1;
}

2. 記錄關機嘗試事件


void __fastcall TForm1::OnWMQueryEndSession(TMessage& Message) {
    SYSTEMTIME st; GetLocalTime(&st);
    AnsiString log = Format("關機嘗試: %02d/%02d/%04d %02d:%02d:%02d - %s",
        ARRAYOFCONST((st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond,
                      CheckBox1->Checked ? "允許" : "攔截")));
    LogToFile(log); // 自訂日誌函數
    
    Message.Result = CheckBox1->Checked ? 1 : 0;
}

3. 區分關機/重開機/登出


void __fastcall TForm1::OnWMQueryEndSession(TMessage& Message) {
    // lParam 低位元組:ENDSESSION_LOGOFF (0x80000000) 表示登出
    // 無法直接區分關機 vs 重開機,需配合 WM_ENDSESSION 或系統事件日誌
    
    if (Message.LParam & ENDSESSION_LOGOFF) {
        // 使用者登出
        Message.Result = AllowLogoff() ? 1 : 0;
    } else {
        // 關機或重開機
        Message.Result = AllowShutdown() ? 1 : 0;
    }
}

關鍵限制與最佳實務

⚠ 重要限制:
  • Windows Vista+ 關機逾時:系統等待應用程式回應最多 **5 秒** (可透過登錄 `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\WaitToKillAppTimeout` 調整,預設 20000ms 但實測較短)。處理邏輯**必須極快**,嚴禁彈出模態對話框、執行 I/O、網路請求。
  • 強制關機:使用者可選擇「強制關機」,系統將忽略所有 `WM_QUERYENDSESSION` 返回值,直接發送 `WM_ENDSESSION(FALSE)` 並終止進程。
  • 服務程式:服務不接收 `WM_QUERYENDSESSION`,需實作 `ServiceMain` 中的 `SERVICE_CONTROL_SHUTDOWN` 處理。
  • 主控台應用:需使用 `SetConsoleCtrlHandler` 處理 `CTRL_SHUTDOWN_EVENT`。

現代替代方案:應用程式復原與重新啟動 (ARR)

Windows 7+ 提供 Application Recovery and Restart 機制,比攔截關機更優雅:


// 註冊復原回呼
RegisterApplicationRecoveryCallback(RecoveryCallback, NULL, 30000, 0);

// 註冊重新啟動命令列
RegisterApplicationRestart(L"/restart", 0);

// 復原回呼範例
DWORD WINAPI RecoveryCallback(LPVOID lpParam) {
    // 儲存未完成工作、狀態資訊
    SaveUnsavedData();
    // 通知系統復原完成
    ApplicationRecoveryFinished(TRUE);
    return 0;
}

優點:系統自動重新啟動應用、還原視窗狀態、不阻塞關機流程、使用者體驗更佳。

測試驗證清單

測試案例預期結果
開始功能表 → 關機視核取方塊狀態允許/攔截
Ctrl+Alt+Del → 關機同前
命令列 `shutdown /s /t 0`同前
遠端桌面 → 登出攔截登出 (若實作區分邏輯)
強制關機 (長按電源鍵 / `shutdown /f`)應用程式被終止,無法攔截
關鍵作業進行中 (旗標=true)攔截關機,日誌記錄

結語

攔截 `WM_QUERYENDSESSION` 是防止使用者誤關機的經典技術,但**必須謹慎使用**。現代 Windows 版本嚴格限制回應時間,且使用者擁有強制關機權限。建議採用「旗標控制 + 非阻塞日誌 + ARR 機制」組合策略:關鍵作業期間設定旗標攔截正常關機,同時註冊復原回呼確保資料不遺失,提供最佳的資料保護與使用者體驗平衡。

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