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

Windows 服務程序 C++ 實戰:SCM 架構、ServiceMain/Handler 協議與線程模型

     編輯:C++ 入門

Windows 服務架構核心

Windows 服務由服務控制管理器 (SCM) 管理,運行於會話 0 (非交互式),核心入口點:

  1. main():註决服務表,啟動分派器。
  2. ServiceMain():服務真正入口,註冊控制處理器、報告狀態、啟動工作線程。
  3. HandlerEx():處理 SCM 控制碼 (啟動/停止/暫停/繼續/關機/自定義)。

完整最小化服務框架代碼


#include 
#include 

SERVICE_STATUS        g_Status = {};
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
HANDLE                g_StopEvent = nullptr;

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI HandlerEx(DWORD ctrlCode, DWORD eventType, LPVOID eventData, LPVOID context);
DWORD WINAPI WorkerThread(LPVOID param);

int _tmain(int argc, TCHAR* argv[]) {
    SERVICE_TABLE_ENTRY dispatchTable[] = {
        { TEXT("MyDemoService"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { nullptr, nullptr }
    };
    if (!StartServiceCtrlDispatcher(dispatchTable)) {
        // 非服務模式啟動時可作調試入口
        return GetLastError();
    }
    return 0;
}

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv) {
    g_StatusHandle = RegisterServiceCtrlHandlerEx(TEXT("MyDemoService"), HandlerEx, nullptr);
    if (!g_StatusHandle) return;

    // 1. 報告啟動掛起
    g_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_Status.dwCurrentState = SERVICE_START_PENDING;
    g_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
    SetServiceStatus(g_StatusHandle, &g_Status);

    // 2. 初始化資源
    g_StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    if (!g_StopEvent) {
        g_Status.dwCurrentState = SERVICE_STOPPED;
        g_Status.dwWin32ExitCode = GetLastError();
        SetServiceStatus(g_StatusHandle, &g_Status);
        return;
    }

    // 3. 啟動工作線程
    HANDLE hThread = CreateThread(nullptr, 0, WorkerThread, nullptr, 0, nullptr);
    if (!hThread) {
        CloseHandle(g_StopEvent);
        g_Status.dwCurrentState = SERVICE_STOPPED;
        g_Status.dwWin32ExitCode = GetLastError();
        SetServiceStatus(g_StatusHandle, &g_Status);
        return;
    }
    CloseHandle(hThread); // 線程句柄無需持有

    // 4. 報告運行狀態
    g_Status.dwCurrentState = SERVICE_RUNNING;
    g_Status.dwCheckPoint = 0;
    g_Status.dwWaitHint = 0;
    SetServiceStatus(g_StatusHandle, &g_Status);
}

VOID WINAPI HandlerEx(DWORD ctrlCode, DWORD eventType, LPVOID eventData, LPVOID context) {
    switch (ctrlCode) {
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
            g_Status.dwCurrentState = SERVICE_STOP_PENDING;
            g_Status.dwCheckPoint = 1;
            g_Status.dwWaitHint = 30000; // 30s 超時
            SetServiceStatus(g_StatusHandle, &g_Status);
            SetEvent(g_StopEvent); // 通知工作線程退出
            return;
        case SERVICE_CONTROL_PAUSE:
            // 暫停邏輯
            g_Status.dwCurrentState = SERVICE_PAUSED;
            SetServiceStatus(g_StatusHandle, &g_Status);
            return;
        case SERVICE_CONTROL_CONTINUE:
            g_Status.dwCurrentState = SERVICE_RUNNING;
            SetServiceStatus(g_StatusHandle, &g_Status);
            return;
        case SERVICE_CONTROL_INTERROGATE:
            SetServiceStatus(g_StatusHandle, &g_Status);
            return;
        default:
            break;
    }
}

DWORD WINAPI WorkerThread(LPVOID param) {
    while (WaitForSingleObject(g_StopEvent, 1000) == WAIT_TIMEOUT) {
        // 實際業務邏輯:輪詢、處理隊列、定時任務等
        // OutputDebugString(TEXT("Working...\n"));
    }
    // 清理資源
    g_Status.dwCurrentState = SERVICE_STOPPED;
    g_Status.dwWin32ExitCode = NO_ERROR;
    SetServiceStatus(g_StatusHandle, &g_Status);
    return 0;
}

關鍵結構體與 API 詳解

SERVICE_TABLE_ENTRY


typedef struct _SERVICE_TABLE_ENTRY {
    LPTSTR lpServiceName;           // 服務名,需與註冊一致
    LPSERVICE_MAIN_FUNCTION lpServiceProc; // ServiceMain 指針
} SERVICE_TABLE_ENTRY;

數組以雙 NULL 結尾。StartServiceCtrlDispatcher 遍歷表,匹配服務名並創建新線程調用對應 ServiceMain。

SERVICE_STATUS 狀態機

狀態含義典型轉換
SERVICE_START_PENDING啟動初始化中→ SERVICE_RUNNING / SERVICE_STOPPED
SERVICE_RUNNING正常運行→ SERVICE_STOP_PENDING / SERVICE_PAUSED
SERVICE_STOP_PENDING正在停止→ SERVICE_STOPPED
SERVICE_PAUSED已暫停→ SERVICE_RUNNING / SERVICE_STOP_PENDING

dwCheckPoint:初始化/停止過程中增量計數,SCM 用於進度顯示。dwWaitHint:預估耗時 (毫秒),超時 SCM 將強制終止進程。

進階主題與最佳實踐

1. 服務恢復機制

配置 ChangeServiceConfig2 設置 SERVICE_CONFIG_FAILURE_ACTIONS,定義失敗時自動重啟/運行命令/重啟電腦。

2. 會話 0 隔離與 UI 交互

Vista+ 服務無法直接顯示 UI。需用互動式服務 (已棄用) 或獨立 GUI 進程 + IPC (命名管道/RPC/共享內存) 通信。

3. 權限與安全描述符

CreateService 時指定 lpServiceStartName (服務帳戶) 和 lpPassword。推薦使用虛擬帳戶 (NT SERVICE\ServiceName) 或組管理服務帳戶 (gMSA),避免密碼管理。

4. 事件日誌記錄


HANDLE hEventLog = RegisterEventSource(nullptr, TEXT("MyDemoService"));
ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, MSG_STARTED, nullptr, 1, 0, &msg, nullptr);
DeregisterEventSource(hEventLog);

5. 調試技巧

  • 註冊表 Image File Execution Options\YourService.exe\Debugger = vsjitdebugger.exe 實現服務啟動即斷點。
  • main() 參數檢測:if (argc > 1 && _tcscmp(argv[1], TEXT("/debug")) == 0) 直接調用 ServiceMain 進入控制台調試模式。

現代替代方案

  • C# .NET Generic Host:IHostedService + BackgroundService,極簡配置、依賴注入、日誌集成。
  • C++ REST SDK (cpprestsdk) / Boost.Asio:用戶態網絡服務,無需 SCM,部署靈活 (systemd/Windows 任務計劃/容器)。
  • Windows 服務 → 容器化:Docker 容器 + Kubernetes DaemonSet 替代傳統服務部署。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved