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

Windows 服務程式設計深度解析(三):控制處理與服務安裝

     編輯:關於C++

服務控制處理函數(CtrlHandler)詳解

前文已介紹服務啟動與 ServiceMain,本文聚焦於最後一個核心組件——CtrlHandler 回呼函數。當呼叫 RegisterServiceCtrlHandler 時,SCM 會保存此函數位址。服務控制程式(SCP)呼叫 Win32 API 要求 SCM 發送控制碼給服務,預定義控制碼如下:

控制碼含義
SERVICE_CONTROL_STOP要求服務停止。需 SERVICE_STOP 存取權。
SERVICE_CONTROL_PAUSE要求服務暫停。需 SERVICE_PAUSE_CONTINUE 存取權。
SERVICE_CONTROL_CONTINUE要求暫停的服務繼續。需 SERVICE_PAUSE_CONTINUE 存取權。
SERVICE_CONTROL_INTERROGATE要求服務立即向 SCM 更新狀態。需 SERVICE_INTERROGATE 存取權。
SERVICE_CONTROL_SHUTDOWN系統關機通知,服務執行最小清理動作。預設逾時約 20 秒(NT 4.0),Win2000 預設 10 秒,可透過登錄檔 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\WaitToKillServiceTimeout(毫秒)調整。
SERVICE_CONTROL_PARAMCHANGE(Win2000) 要求服務重讀啟動參數。
SERVICE_CONTROL_NETBINDCHANGE(Win2000) 網路綁定變更通知。
SERVICE_CONTROL_NETBINDREMOVE(Win2000) 網路綁定元件移除通知。
SERVICE_CONTROL_NETBINDENABLE(Win2000) 網路綁定啟用通知。
SERVICE_CONTROL_NETBINDDISABLE(Win2000) 網路綁定停用通知。

此外,服務亦可接受 128~255 之間的使用者自訂控制碼。

處理狀態變更控制碼

收到 STOP、PAUSE、CONTINUE 時,必須呼叫 SetServiceStatus 確認並指定處理所需時間。例如收到停止請求:

  1. 將 dwCurrentState 設為 SERVICE_STOP_PENDING,讓 SCM 知悉已接收。
  2. 若無法立即完成(如等待網路 I/O 或資料寫入磁碟),透過 dwCheckPoint 和 dwWaitHint 回報預估進度,必要時週期性呼叫 SetServiceStatus 更新。
  3. 操作完成後,再次呼叫 SetServiceStatus,將 dwCurrentState 設為 SERVICE_STOPPED,並將 dwCheckPoint、dwWaitHint 歸零。暫停/繼續同理。

處理 INTERROGATE 與 SHUTDOWN

  • INTERROGATE:直接將 dwCurrentState 設為當前狀態,dwCheckPoint、dwWaitHint 歸零,呼叫 SetServiceStatus 即可。
  • SHUTDOWN:系統即將關機,服務無須回應狀態,僅執行最小必要的資料保存動作,確保機器能及時關機。

使用者自訂控制碼與長時間操作策略

收到自訂碼時執行對應動作,除非該動作強制改變服務狀態(如觸發停止),否則不呼叫 SetServiceStatus。若 CtrlHandler 執行超過 30 秒未返回,SCM 會回報錯誤。解決方案:在 CtrlHandler 中建立新執行緒處理耗時操作,主函數迅速返回並回報 SERVICE_STOP_PENDING;新執行緒完成後再設為 SERVICE_STOPPED。若耗時可預估,仍建議用 dwCheckPoint/dwWaitHint 機制。

服務安裝:CreateService 與 OpenSCManager

服務程式可呼叫 CreateService 將資訊寫入 SCM 資料庫:

SC_HANDLE CreateService(
    SC_HANDLE hSCManager,           // SCM 資料庫控制碼
    LPCTSTR lpServiceName,          // 服務名稱
    LPCTSTR lpDisplayName,          // 顯示名稱
    DWORD dwDesiredAccess,          // 存取權限
    DWORD dwServiceType,            // 服務類型
    DWORD dwStartType,              // 啟動類型
    DWORD dwErrorControl,           // 錯誤控制
    LPCTSTR lpBinaryPathName,       // 執行檔路徑
    LPCTSTR lpLoadOrderGroup,       // 載入順序群組
    LPDWORD lpdwTagId,              // 標識符(驅動程式用)
    LPCTSTR lpDependencies,         // 相依服務/群組
    LPCTSTR lpServiceStartName,     // 啟動帳號
    LPCTSTR lpPassword              // 帳號密碼
);

取得 SCM 控制碼:OpenSCManager

SC_HANDLE OpenSCManager(
    LPCTSTR lpMachineName,      // 目標電腦名稱,NULL 為本機,遠端需以 "\\" 開頭
    LPCTSTR lpDatabaseName,     // 資料庫名稱,通常傳 NULL(預設 SERVICES_ACTIVE_DATABASE)
    DWORD dwDesiredAccess       // 存取權限
);

常用權限:SC_MANAGER_ALL_ACCESS(需管理員權限)、SC_MANAGER_CONNECT、SC_MANAGER_ENUMERATE_SERVICE、SC_MANAGER_QUERY_LOCK_STATUS。返回控制碼可用 CloseServiceHandle 關閉。

CreateService 關鍵參數說明

  • lpServiceName / lpDisplayName:服務實際名稱與「服務」管理工具顯示名稱。
  • dwDesiredAccess:服務物件存取權限,安裝時通常傳 SERVICE_ALL_ACCESS。
  • dwServiceType:SERVICE_WIN32_OWN_PROCESS(獨立進程)、SERVICE_WIN32_SHARE_PROCESS(共用進程)、SERVICE_INTERACTIVE_PROCESS(與桌面互動,不建議)。
  • dwStartType:SERVICE_AUTO_START(自動)、SERVICE_DEMAND_START(手動)、SERVICE_DISABLED(停用)。另有兩項供驅動程式使用。
  • dwErrorControl:啟動失敗時的處理等級——IGNORE(記錄繼續)、NORMAL(記錄彈窗繼續)、SEVERE(嘗試最後正確配置重啟)、CRITICAL(嚴重,嘗試最後正確配置仍失敗則啟動失敗)。
  • lpBinaryPathName:路徑含空格時必須加雙引號,如 "d:\my share\myservice.exe" 寫成 "\"d:\my share\myservice.exe\""。
  • lpLoadOrderGroup / lpdwTagId:指定載入順序群組,非驅動程式通常傳 NULL。
  • lpDependencies:相依服務或群組名稱陣列。指定群組時需加 SC_GROUP_IDENTIFIER 前綴。無相依傳 NULL。
  • lpServiceStartName / lpPassword:啟動帳號格式 Domain\User 或 .\User(本機)。NULL 表使用 LocalSystem。SERVICE_INTERACTIVE_PROCESS 必須用 LocalSystem。密碼為 NULL(系統帳號)或空字串(無密碼)。

服務基礎原理到此告一段落,但實務上仍有許多進階主題(如服務復原、觸發啟動、Event Tracing 等)待探討,敬請期待後續文章。

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