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

VC++ 建立 Windows 服務程式完整教學:從專案建立到安裝控制

     編輯:VC 教學

前言

Windows 服務在背景執行、開機自動啟動、無需使用者登入,適合監控、備份、伺服器類程式。本文以 Win32 應用程式專案為基礎,逐步建立完整服務框架,包含安裝、解除安裝、主執行緒、控制處理與狀態回報。

專案建立

  1. 新建 Win32 Application,名稱 ServiceTest,選 Empty Project。
  2. 加入 ServiceTest.cpp 作為唯一原始檔。

核心全域變數與宣告


#include <windows.h>
#include <winsvc.h>
#include <tchar.h>

TCHAR g_szSvcName[] = _T("ServiceTest");
SERVICE_STATUS_HANDLE g_hStatus = NULL;
SERVICE_STATUS g_status = {0};
DWORD g_dwThreadId = 0;

BOOL InstallService();
BOOL UninstallService();
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
VOID LogEvent(LPCTSTR fmt, ...);

初始化狀態結構


void InitStatus()
{
    g_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_status.dwCurrentState = SERVICE_STOPPED;
    g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_status.dwWin32ExitCode = 0;
    g_status.dwCheckPoint = 0;
    g_status.dwWaitHint = 0;
}

安裝服務


BOOL InstallService()
{
    TCHAR szPath[MAX_PATH];
    GetModuleFileName(NULL, szPath, MAX_PATH);

    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!hSCM) return FALSE;

    SC_HANDLE hSvc = CreateService(hSCM, g_szSvcName, g_szSvcName,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
        szPath, NULL, NULL, _T(""), NULL, NULL);

    BOOL ok = (hSvc != NULL);
    if (hSvc) CloseServiceHandle(hSvc);
    CloseServiceHandle(hSCM);
    return ok;
}

解除安裝服務


BOOL UninstallService()
{
    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!hSCM) return FALSE;

    SC_HANDLE hSvc = OpenService(hSCM, g_szSvcName, SERVICE_STOP | DELETE);
    if (!hSvc) { CloseServiceHandle(hSCM); return FALSE; }

    ControlService(hSvc, SERVICE_CONTROL_STOP, &g_status);
    BOOL ok = DeleteService(hSvc);

    CloseServiceHandle(hSvc);
    CloseServiceHandle(hSCM);
    return ok;
}

服務主執行緒


VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
    g_status.dwCurrentState = SERVICE_START_PENDING;
    g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;

    g_hStatus = RegisterServiceCtrlHandler(g_szSvcName, ServiceCtrlHandler);
    if (!g_hStatus) { LogEvent(_T("Handler 註冊失敗")); return; }

    SetServiceStatus(g_hStatus, &g_status);

    g_status.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(g_hStatus, &g_status);

    // --- 實際工作迴圈 ---
    while (g_status.dwCurrentState == SERVICE_RUNNING) {
        // 執行核心任務:監控、備份、網路服務等
        Sleep(1000);
    }
    // ----------------------

    g_status.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(g_hStatus, &g_status);
}

控制處理函式


VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl)
{
    switch (dwCtrl) {
    case SERVICE_CONTROL_STOP:
        g_status.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(g_hStatus, &g_status);
        // 通知工作迴圈結束(可用事件物件)
        break;
    case SERVICE_CONTROL_INTERROGATE:
        break;
    }
    SetServiceStatus(g_hStatus, &g_status);
}

主程式進入點


int APIENTRY _tWinMain(HINSTANCE hInst, HINSTANCE, LPTSTR lpCmdLine, int)
{
    InitStatus();

    if (_tcsicmp(lpCmdLine, _T("/install")) == 0) {
        InstallService();
        return 0;
    }
    if (_tcsicmp(lpCmdLine, _T("/uninstall")) == 0) {
        UninstallService();
        return 0;
    }

    SERVICE_TABLE_ENTRY dispatchTable[] = {
        { g_szSvcName, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { NULL, NULL }
    };

    if (!StartServiceCtrlDispatcher(dispatchTable)) {
        LogEvent(_T("StartServiceCtrlDispatcher 失敗: %d"), GetLastError());
    }
    return 0;
}

建置與測試

  1. 建置 Release 版本產生 ServiceTest.exe。
  2. 以管理員身分開啟命令提示字元:
ServiceTest.exe /install    # 安裝服務
net start ServiceTest       # 啟動服務
net stop ServiceTest        # 停止服務
ServiceTest.exe /uninstall  # 移除服務

關鍵提醒

  • dwControlsAccepted 必須包含 SERVICE_ACCEPT_STOP,否則服務管理員無法發送停止命令。
  • 服務啟動後若 30 秒內未回報 SERVICE_RUNNING,SCM 會判定啟動失敗;長時間初始化需定期 SetServiceStatus 更新 dwCheckPoint 與 dwWaitHint。
  • 服務執行於 Session 0,無法直接存取使用者桌面(UI0 隔離),需用命名管道、RPC 或共用記憶體與前端溝通。
  • 除錯技巧:在 ServiceMain 開頭加 DebugBreak(),附加偵錯器至服務進程。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved