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

Win32 服務程序完整實例:從服務安裝、啟動到控制的 C/C++ 實現

     編輯:關於C語言

Win32 服務程序完整實例:服務主程序與控制程序一體化實現

Windows 服務由服務主程序(在後台運行的業務邏輯)和服務控制程序(安裝、啟動、停止、刪除服務)組成。本文提供一個完整、可編譯運行的單文件實例,展示如何將兩者融合在一個 .exe 中,通過命令行參數控制服務生命週期。

架構設計

  • main():標準入口,解析參數(-create/-start/-stop/-delete),或啟動服務調度器。
  • ServiceMain():服務入口,註冊控制處理器,更新狀態,進入工作循環。
  • ControlHandler():處理 SCM 發來的控制碼(停止、關機)。
  • MyWork():業務邏輯(示例中彈出 MessageBox)。
  • OnCreate/Start/Stop/Delete():服務控制操作封裝。

完整核心代碼

#define WIN32_LEAN_AND_MEAN
#include 
#include 
#include 

#define SLEEP_TIME 5000
#define SERVICE_NAME "ServiTest"
#define SERVICE_DISPLAY_NAME "Test Service"
#define SERVICE_DESC "Demo Win32 Service"

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
void Log(const char* filename, const char* str);
int MyWork();
void OnCreate(); void OnStart(); void OnStop(); void OnDelete();

int main(int argc, char* argv[]) {
    SERVICE_TABLE_ENTRY ServiceTable[2] = {
        { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { NULL, NULL }
    };

    if (argc == 2) {
        if (!stricmp(argv[1], "-create")) { OnCreate(); return 0; }
        if (!stricmp(argv[1], "-start"))  { OnStart();  return 0; }
        if (!stricmp(argv[1], "-stop"))   { OnStop();   return 0; }
        if (!stricmp(argv[1], "-delete")) { OnDelete(); return 0; }
        printf("Invalid parameter\n"); return 0;
    }

    // 作為服務運行
    StartServiceCtrlDispatcher(ServiceTable);
    return 0;
}

// === 服務主程序 ===
void ServiceMain(int argc, char** argv) {
    ServiceStatus.dwServiceType = SERVICE_WIN32;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    ServiceStatus.dwWin32ExitCode = 0;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;

    hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, (LPHANDLER_FUNCTION)ControlHandler);
    if (!hStatus) return;

    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hStatus, &ServiceStatus);

    while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
        if (MyWork()) {  // 返回非0 表示需停止
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus.dwWin32ExitCode = -1;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        }
    }
}

int MyWork() {
    MessageBox(NULL, "Service Working...", SERVICE_NAME, MB_OK);
    Sleep(SLEEP_TIME);
    return 0;
}

void ControlHandler(DWORD request) {
    switch (request) {
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
            Log("C:\\MemoryWatch.txt", "Monitoring stopped.");
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus.dwWin32ExitCode = 0;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
    }
    SetServiceStatus(hStatus, &ServiceStatus);
}

// === 服務控制程序 ===
void OnCreate() {
    char path[MAX_PATH];
    GetModuleFileName(NULL, path, MAX_PATH);

    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) { printf("OpenSCManager failed: %d\n", GetLastError()); return; }

    SC_HANDLE svc = CreateService(
        scm, SERVICE_NAME, SERVICE_DISPLAY_NAME, SERVICE_ALL_ACCESS,
        SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
        SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
        path, NULL, NULL, NULL, NULL, NULL
    );

    if (!svc) {
        DWORD err = GetLastError();
        if (err != ERROR_SERVICE_EXISTS) printf("CreateService failed: %d\n", err);
        else printf("Service already exists.\n");
    } else {
        printf("Service created successfully.\n");
        CloseServiceHandle(svc);
    }
    CloseServiceHandle(scm);
}

void OnStart() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) return;
    SC_HANDLE svc = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS);
    if (svc) {
        if (!StartService(svc, 0, NULL)) {
            DWORD err = GetLastError();
            if (err != ERROR_SERVICE_ALREADY_RUNNING) printf("StartService failed: %d\n", err);
            else printf("Already running.\n");
        } else {
            printf("Starting...");
            // 等待服務進入 RUNNING 狀態
            while (QueryServiceStatus(svc, &ServiceStatus)) {
                if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING) Sleep(100);
                else break;
            }
        }
        CloseServiceHandle(svc);
    }
    CloseServiceHandle(scm);
}

void OnStop() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) return;
    SC_HANDLE svc = OpenService(scm, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
    if (svc) {
        QueryServiceStatus(svc, &ServiceStatus);
        if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
            ControlService(svc, SERVICE_CONTROL_STOP, &ServiceStatus);
        }
        CloseServiceHandle(svc);
    }
    CloseServiceHandle(scm);
}

void OnDelete() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) return;
    SC_HANDLE svc = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS);
    if (svc) {
        QueryServiceStatus(svc, &ServiceStatus);
        if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
            ControlService(svc, SERVICE_CONTROL_STOP, &ServiceStatus);
        }
        DeleteService(svc);
        CloseServiceHandle(svc);
    }
    CloseServiceHandle(scm);
}

void Log(const char* file, const char* str) {
    FILE* fp = fopen(file, "a+");
    if (fp) { fprintf(fp, "%s\n", str); fclose(fp); }
}

編譯與運行步驟

  1. 使用 Visual Studio 或 MinGW-w64 編譯為 ServiceDemo.exe。
  2. 以管理員身份打開 CMD,切換到 EXE 目錄。
  3. 依次執行:
ServiceDemo.exe -create   # 安裝服務
ServiceDemo.exe -start    # 啟動服務
# 觀察彈出的 MessageBox(需勾選服務屬性「允許服務與桌面交互」)
ServiceDemo.exe -stop     # 停止服務
ServiceDemo.exe -delete   # 卸載服務

關鍵技術點

  • SERVICE_INTERACTIVE_PROCESS:允許服務彈出 UI(僅限本地系統賬戶,現代 Windows 建議避免 GUI 交互)。
  • 狀態機:SERVICE_START_PENDING → SERVICE_RUNNING → SERVICE_STOPPED,必須及時 SetServiceStatus,否則 SCM 會判定服務掛起並強制終止。
  • 錯誤處理:所有 API 調用後檢查返回值,GetLastError() 定位問題(常見:權限不足、服務已存在、路徑錯誤)。
  • 單文件雙模式:argc==2 走控制邏輯,否則走服務調度,部署極簡。

生產環境建議

  1. 業務邏輯移至獨立線程,ServiceMain 僅負責狀態管理。
  2. 使用 EventLog 替代文件日誌。
  3. 服務賬戶改為專用最小權限賬戶,非 LocalSystem。
  4. 添加 SERVICE_CONTROL_PAUSE/CONTINUE 支持。
  5. 配置恢復動作(ChangeServiceConfig2 + SERVICE_CONFIG_FAILURE_ACTIONS)。

此例為學習 Win32 服務機制的最小完整樣本,掌握後可擴展為健壯的生產級服務程序。

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