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

Windows 服務開發深度解析 (三):控制處理器 (CtrlHandler) 完整指南

     編輯:VC 教學

核心角色:服務控制處理器 (ServiceCtrlHandler)

服務控制管理器 (SCM) 透過 RegisterServiceCtrlHandler 註冊的回調函數 `CtrlHandler` 向服務發送控制碼。服務必須在這裡解析指令、更新狀態、協調工作線程完成相應操作。本文詳細解析所有預定義控制碼、狀態回報機制、關機處理邏輯與進階配置。

完整控制碼對照表 (Win32 API)

控制碼常數數值含義與要求
SERVICE_CONTROL_STOP0x0001請求停止服務。需 SERVICE_STOP 權限。服務必須回報 SERVICE_STOP_PENDING 並最終轉為 SERVICE_STOPPED。
SERVICE_CONTROL_PAUSE0x0002請求暫停服務。需 SERVICE_PAUSE_CONTINUE 權限。回報 SERVICE_PAUSE_PENDING → SERVICE_PAUSED。
SERVICE_CONTROL_CONTINUE0x0003請求恢復暫停服務。需 SERVICE_PAUSE_CONTINUE 權限。回報 SERVICE_CONTINUE_PENDING → SERVICE_RUNNING。
SERVICE_CONTROL_INTERROGATE0x0004請求立即回報當前狀態。需 SERVICE_INTERROGATE 權限。直接回報當前 dwCurrentState,dwCheckPoint=0, dwWaitHint=0。
SERVICE_CONTROL_SHUTDOWN0x0005系統關機通知。服務執行最小清理,**無需回報狀態** (系統即將關閉)。預設超時 20 秒 (NT4) / 10 秒 (Win2K+),可調 HKLM\SYSTEM\CurrentControlSet\Control\WaitToKillServiceTimeout (毫秒)。
SERVICE_CONTROL_PARAMCHANGE0x0006Win2K+:請求重讀啟動參數。需 SERVICE_PAUSE_CONTINUE。
SERVICE_CONTROL_NETBINDCHANGE0x0007Win2K+:網路綁定變更,需重讀綁定資訊。
SERVICE_CONTROL_NETBINDREMOVE0x0008Win2K+:網路綁定組件移除,需解除綁定。
SERVICE_CONTROL_NETBINDENABLE0x0009Win2K+:禁用綁定被啟用,需添加綁定。
SERVICE_CONTROL_NETBINDDISABLE0x000AWin2K+:綁定被禁用,需移除綁定。
用戶自定義128-255服務自定義控制碼範圍,擴展業務邏輯 (如重載配置、觸發備份、切換日誌級別)。

狀態回報核心:SetServiceStatus 與 SERVICE_STATUS 結構

typedef struct _SERVICE_STATUS {
    DWORD dwServiceType;        // SERVICE_WIN32_OWN_PROCESS 等
    DWORD dwCurrentState;       // 當前狀態 (見下表)
    DWORD dwControlsAccepted;   // 接受的控制碼位掩碼
    DWORD dwWin32ExitCode;      // 錯誤碼 (NO_ERROR 或錯誤)
    DWORD dwServiceSpecificExitCode; // 服務特定錯誤碼
    DWORD dwCheckPoint;         // 進度檢查點 (遞增)
    DWORD dwWaitHint;           // 預估等待時間 (毫秒)
} SERVICE_STATUS;

狀態碼 (dwCurrentState) 速查

狀態常數值含義
SERVICE_STOPPED0x0001已停止
SERVICE_START_PENDING0x0002啟動中
SERVICE_STOP_PENDING0x0003停止中
SERVICE_RUNNING0x0004正在運行
SERVICE_CONTINUE_PENDING0x0005恢復中
SERVICE_PAUSE_PENDING0x0006暫停中
SERVICE_PAUSED0x0007已暫停

關鍵場景處理流程圖解

1. 處理 STOP / PAUSE / CONTINUE (需進度回報)

VOID WINAPI CtrlHandler(DWORD dwControl) {
    switch (dwControl) {
        case SERVICE_CONTROL_STOP:
            // 1. 立即回報 STOP_PENDING,告知 SCM 已收到
            g_status.dwCurrentState = SERVICE_STOP_PENDING;
            g_status.dwCheckPoint = 1;
            g_status.dwWaitHint = 30000; // 預估 30 秒內完成
            SetServiceStatus(g_statusHandle, &g_status);
            
            // 2. 通知工作線程優雅退出 (事件/標誌/IOCP 完成端口)
            SignalWorkerThreadsToStop();
            
            // 3. (可選) 週期性更新進度
            // while (WorkersStillRunning()) {
            //     g_status.dwCheckPoint++;
            //     SetServiceStatus(g_statusHandle, &g_status);
            //     Sleep(1000);
            // }
            
            // 4. 所有清理完成,最終回報 STOPPED
            g_status.dwCurrentState = SERVICE_STOPPED;
            g_status.dwCheckPoint = 0;
            g_status.dwWaitHint = 0;
            g_status.dwWin32ExitCode = NO_ERROR;
            SetServiceStatus(g_statusHandle, &g_status);
            break;
            
        case SERVICE_CONTROL_PAUSE:
            // 類似邏輯:PAUSE_PENDING → 通知線程暫停 → PAUSED
            break;
        case SERVICE_CONTROL_CONTINUE:
            // CONTINUE_PENDING → 通知線程恢復 → RUNNING
            break;
    }
}

2. 處理 INTERROGATE (即時狀態查詢)

case SERVICE_CONTROL_INTERROGATE:
    // 直接回報當前狀態,無需改變 CheckPoint/WaitHint
    SetServiceStatus(g_statusHandle, &g_status);
    break;

3. 處理 SHUTDOWN (系統關機,分秒必爭)

case SERVICE_CONTROL_SHUTDOWN:
    // **不要**呼叫 SetServiceStatus 回報狀態!SCM 不會等待。
    // 僅執行「最小必要清理」:刷新關鍵數據到磁碟、關閉資料庫連線、釋放獨佔資源。
    // 避免:網路請求、複雜計算、等待子進程、UI 彈窗。
    PerformEmergencyCheckpoint(); // 如: FlushViewOfFile, CommitTransaction
    break;

關機超時配置:WaitToKillServiceTimeout

註冊表路徑:HKLM\SYSTEM\CurrentControlSet\Control
值名:WaitToKillServiceTimeout (REG_SZ,毫秒字串,預設 "10000" = 10 秒)
**注意**:此為全局設定,影響所有服務。服務自身不可程式化修改此值 (需管理員權限),但可在安裝腳本中建議調整。

進階:接受自定義控制碼擴展業務


// 服務啟動時聲明接受的控制碼 (含自定義 128-255)
g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE 
                            | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PARAMCHANGE
                            | SERVICE_ACCEPT_NETBINDCHANGE
                            | (1 << (MY_CUSTOM_RELOAD_CONFIG - 128)); // 自定義位

// CtrlHandler 中處理
case MY_CUSTOM_RELOAD_CONFIG: // 定義為 128
    ReloadConfigurationFile();
    // 可選:回報 INTERROGATE 風格狀態,或不回報
    break;

最佳實踐檢查清單

  1. ✅ **所有狀態轉換必經 SetServiceStatus**,否則 SCM 視為掛起並強制終止。
  2. ✅ **dwCheckPoint 遞增**、**dwWaitHint 合理估算** (含緩衝),避免 SCM 超時誤殺。
  3. ✅ **工作線程與 CtrlHandler 分離**:CtrlHandler 只負責狀態回報與發信號,耗時操作在工作線程完成。
  4. ✅ **SHUTDOWN 處理極簡化**,不調用 SetServiceStatus。
  5. ✅ **錯誤碼規範**:dwWin32ExitCode 用系統錯誤碼;服務私有錯誤用 dwServiceSpecificExitCode。
  6. ✅ **註冊表參數讀取** 放在 ServiceMain 初始化階段,PARAMCHANGE 觸發重讀。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved