SERVICE_STOPPED → START_PENDING → RUNNING → STOP_PENDING → STOPPED,以及 PAUSE_PENDING/PAUSED/CONTINUE_PENDING。SERVICE_TABLE_ENTRY 分派表,呼叫 StartServiceCtrlDispatcher() 進入 SCM 控制循環。RegisterServiceCtrlHandler(),回報 SERVICE_START_PENDING → 初始化 → 回報 SERVICE_RUNNING → 進入工作迴圈。STOP, SHUTDOWN, PAUSE, CONTINUE, INTERROGATE),更新狀態、通知工作迴圈退出。sc.exe 或 CreateService() API 註冊至 SCM 資料庫。
#include <windows.h>
#include <stdio.h>
#define SLEEP_MS 5000
#define LOG_FILE "C:\\svc_log.txt"
SERVICE_STATUS g_status = {0};
SERVICE_STATUS_HANDLE g_status_handle = NULL;
volatile BOOL g_running = FALSE;
void write_log(const char *msg) {
FILE *f; fopen_s(&f, LOG_FILE, "a+");
if (f) { fprintf(f, "%s\n", msg); fclose(f); }
}
VOID WINAPI CtrlHandler(DWORD ctrl) {
switch (ctrl) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
g_running = FALSE;
g_status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(g_status_handle, &g_status);
break;
default: break;
}
}
VOID WINAPI ServiceMain(DWORD argc, LPSTR *argv) {
g_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_status.dwCurrentState = SERVICE_START_PENDING;
g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
g_status_handle = RegisterServiceCtrlHandler("MyDemoSvc", CtrlHandler);
if (!g_status_handle) { write_log("RegisterHandler failed"); return; }
write_log("Service starting...");
g_status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_status_handle, &g_status);
g_running = TRUE;
MEMORYSTATUSEX mem = { .dwLength = sizeof(mem) };
char buf[128];
while (g_running) {
GlobalMemoryStatusEx(&mem);
sprintf_s(buf, sizeof(buf), "Avail RAM: %llu MB", mem.ullAvailPhys / 1024 / 1024);
write_log(buf);
Sleep(SLEEP_MS);
}
write_log("Service stopped.");
g_status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_status_handle, &g_status);
}
int main() {
SERVICE_TABLE_ENTRY dispatch[] = {
{ "MyDemoSvc", (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher(dispatch)) {
// 若直接雙擊執行,會失敗(錯誤 1063),此時可作為主控台測試邏輯
return GetLastError();
}
return 0;
}
rem 管理員權限 CMD
sc create MyDemoSvc binPath= "C:\full\path\MyDemoSvc.exe" start= auto
sc start MyDemoSvc
sc query MyDemoSvc
sc stop MyDemoSvc
sc delete MyDemoSvc
注意:binPath= 後有空格,路徑含空格需雙引號包裹。
RegisterEventSource/ReportEvent 寫入系統日誌,替代文字檔。sc failure 設定失敗自動重啟、執行指令。CServiceBase 類別封裝狀態機、執行緒池、配置熱載入,參考 Microsoft ATL 服務範本或開源 winsvcpp。StartServiceCtrlDispatcher 只有在 SCM 啟動時才成功。開發期可加 if (argc>1 && strcmp(argv[1],"-console")==0) run_console_mode();。ServiceMain 做耗時同步 I/O,應另開執行緒或用非阻塞/IOCP。C:\ 需管理員權限,建議用 %ALLUSERSPROFILE%\MySvc\logs。SetServiceStatus 必須及時,否則 SCM 判定服務掛起並強制終止。