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

C++Builder 開發 Windows 服務完整指南:從 SCM 到服務主程式架構

     編輯:關於C++

Windows 服務架構核心組件

  • 服務控制管理器 (SCM):管理服務資料庫、啟動/停止/查詢服務、維護服務狀態。
  • 服務主程式:Console 應用程式,入口點 main() → StartServiceCtrlDispatcher 連接 SCM。
  • 服務控制程式:GUI 應用程式,呼叫 OpenSCManager、CreateService、StartService、ControlService 管理服務生命週期。

核心 SCM API 速查表

API用途關鍵參數
OpenSCManager打開 SCM 資料庫機器名、資料庫名、存取權限 (SC_MANAGER_CREATE_SERVICE 等)
CreateService建立服務服務名、顯示名、類型 (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS)、啟動類型 (SERVICE_AUTO_START)、二進位路徑
OpenService打開既有服務服務名、存取權限
StartService啟動服務服務句柄、啟動參數
ControlService發送控制碼SERVICE_CONTROL_STOP/PAUSE/CONTINUE/INTERROGATE/SHUTDOWN
QueryServiceStatus查詢服務狀態填充 SERVICE_STATUS 結構
DeleteService刪除服務需先停止服務

服務主程式三大核心函數

1. main():連接 SCM

void WINAPI ServiceMain(DWORD, LPTSTR*);  // 前向宣告
void WINAPI Handler(DWORD);               // 前向宣告

int main() {
    SERVICE_TABLE_ENTRY ste[2] = {
        { "MyService", ServiceMain },
        { NULL, NULL }  // 結束標記
    };
    StartServiceCtrlDispatcher(ste);  // 阻塞直到服務停止
    return 0;
}

2. ServiceMain():服務真正入口,註冊 Handler、報告狀態、執行業務


SERVICE_STATUS_HANDLE g_ssh;
SERVICE_STATUS g_ss;

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
    // 1. 註冊控制處理函數
    g_ssh = RegisterServiceCtrlHandler("MyService", Handler);
    
    // 2. 初始化狀態:啟動中
    g_ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
    g_ss.dwCurrentState = SERVICE_START_PENDING;
    g_ss.dwControlsAccepted = 0;  // 初始化階段不接受控制
    g_ss.dwWin32ExitCode = NO_ERROR;
    g_ss.dwCheckPoint = 0;
    g_ss.dwWaitHint = 3000;  // 3 秒超時
    SetServiceStatus(g_ssh, &g_ss);

    // 3. 執行初始化工作 (資源載入、開啟埠、連線資料庫...)
    if (!InitService()) {
        g_ss.dwCurrentState = SERVICE_STOPPED;
        g_ss.dwWin32ExitCode = GetLastError();
        SetServiceStatus(g_ssh, &g_ss);
        return;
    }

    // 4. 進入運行狀態
    g_ss.dwCurrentState = SERVICE_RUNNING;
    g_ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
    g_ss.dwCheckPoint = 0;
    g_ss.dwWaitHint = 0;
    SetServiceStatus(g_ssh, &g_ss);

    // 5. 業務主迴圈 (或啟動工作線程,此處保持存活)
    RunServiceLoop();  // 例如: while (g_running) { Sleep(1000); DoWork(); }

    // 6. 清理與停止
    CleanupService();
    g_ss.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(g_ssh, &g_ss);
}

3. Handler():處理 SCM 發送的控制碼


void WINAPI Handler(DWORD opcode) {
    switch (opcode) {
        case SERVICE_CONTROL_STOP:
            g_ss.dwCurrentState = SERVICE_STOP_PENDING;
            g_ss.dwCheckPoint = 1;
            g_ss.dwWaitHint = 5000;
            SetServiceStatus(g_ssh, &g_ss);
            SignalStop();  // 通知主迴圈/工作線程退出
            break;
        case SERVICE_CONTROL_PAUSE:
            g_ss.dwCurrentState = SERVICE_PAUSED;
            SetServiceStatus(g_ssh, &g_ss);
            PauseWork();
            break;
        case SERVICE_CONTROL_CONTINUE:
            g_ss.dwCurrentState = SERVICE_RUNNING;
            SetServiceStatus(g_ssh, &g_ss);
            ResumeWork();
            break;
        case SERVICE_CONTROL_INTERROGATE:
            SetServiceStatus(g_ssh, &g_ss);  // 立即回報當前狀態
            break;
        case SERVICE_CONTROL_SHUTDOWN:
            // 系統關機,同 STOP 但時間更緊
            Handler(SERVICE_CONTROL_STOP);
            break;
    }
}

服務控制程式 (GUI) 實現要點


// 安裝服務
void InstallService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
    SC_HANDLE svc = CreateService(scm, "MyService", "My Service",
        SERVICE_ALL_ACCESS,
        SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
        SERVICE_AUTO_START,  // 自動啟動
        SERVICE_ERROR_NORMAL,
        "C:\\Path\\MyService.exe",  // 絕對路徑!
        NULL, NULL, NULL, NULL, NULL);
    CloseServiceHandle(svc); CloseServiceHandle(scm);
}

// 啟動服務
void StartService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
    SC_HANDLE svc = OpenService(scm, "MyService", SERVICE_START);
    StartService(svc, 0, NULL);
    CloseServiceHandle(svc); CloseServiceHandle(scm);
}

// 停止服務
void StopService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
    SC_HANDLE svc = OpenService(scm, "MyService", SERVICE_STOP | SERVICE_QUERY_STATUS);
    SERVICE_STATUS ss;
    ControlService(svc, SERVICE_CONTROL_STOP, &ss);
    CloseServiceHandle(svc); CloseServiceHandle(scm);
}

// 移除服務
void RemoveService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
    SC_HANDLE svc = OpenService(scm, "MyService", DELETE);
    SERVICE_STATUS ss;
    ControlService(svc, SERVICE_CONTROL_STOP, &ss);  // 先停止
    DeleteService(svc);
    CloseServiceHandle(svc); CloseServiceHandle(scm);
}

C++Builder 專屬專案設置

  1. 專案類型:Console Application (服務主程式) / VCL Forms Application (控制程式)。
  2. 連結選項:服務主程式需連結 advapi32.lib (C++Builder 自動處理)。
  3. 進入點:服務主程式保留 main(),非 WinMain。
  4. 互動式服務:SERVICE_INTERACTIVE_PROCESS 允許服務顯示 UI (Win2000/XP 有效,**Vista+ Session 0 隔離導致 UI 不顯示**,生產環境**不建議**依賴)。
  5. 除錯技巧:在 main() 開頭加 if (IsDebuggerPresent()) DebugBreak(); 或命令列參數區分 MyService.exe -debug 以 Console 模式運行業務邏輯,便於除錯。

常見坑與避坑

問題原因解決
服務啟動後立即停止 (1053 錯誤)ServiceMain 未在 30 秒內回報 RUNNING初始化耗時工作放入工作線程,ServiceMain 快速回報 RUNNING
控制程式無法安裝/啟動服務權限不足 (需管理員)、路徑含空格未加引號、服務名衝突以管理員運行、路徑加雙引號、檢查 GetLastError()
服務無法存取網路資源/列印機預設 LocalSystem 帳號無網路憑證CreateService 指定 lpServiceStartName 為網域帳號、或服務內用 WNetAddConnection2
Session 0 隔離導致 UI 不顯示Vista+ 服務運行於 Session 0,無法與用戶桌面互動改用「服務 + 用戶端 GUI 通訊 (Named Pipe/RPC/TCP)」架構
服務停止不乾淨,資源洩漏Handler 未正確通知工作線程、主迴圈未檢查停止旗標用 Event/原子旗標通知、工作線程定期檢查、CleanupService 釋放所有資源

現代化建議:C++17/20 + 獨立庫

  • 將業務邏輯封裝為 **無依賴的靜態庫/動態庫**,服務主程式僅作薄適配層。
  • 使用 std::thread、std::atomic、std::condition_variable 替代 Win32 同步物件。
  • 日誌採用 **spdlog** 等非同步日誌庫,支援滾動、壓縮、遠端傳送。
  • 配置檔用 **TOML/JSON** (nlohmann/json),命令列解析用 **CLI11**。
  • 單元測試覆蓋業務邏輯,服務主程式僅做整合測試。

總結

  1. Windows 服務 = **SCM 管理的長期運行進程**,核心三函數:main → StartServiceCtrlDispatcher、ServiceMain、Handler。
  2. 服務主程式**必須快速回報狀態**:START_PENDING → RUNNING,耗時初始化放背景線程。
  3. 控制程式透過 **SCM API** 完成安裝/啟動/停止/移除全生命週期管理。
  4. C++Builder 開發服務:**Console 專案 + advapi32.lib**,注意 Session 0 隔離、管理員權限、路徑處理。
  5. 現代架構:**業務邏輯庫化 + 薄適配層 + 非同步日誌 + 標準庫同步**,提升可測試性與可維護性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved