Windows 服務由服務控制管理器 (SCM) 管理,運行於會話 0 (非交互式),核心入口點:
main():註决服務表,啟動分派器。ServiceMain():服務真正入口,註冊控制處理器、報告狀態、啟動工作線程。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;
}
typedef struct _SERVICE_TABLE_ENTRY {
LPTSTR lpServiceName; // 服務名,需與註冊一致
LPSERVICE_MAIN_FUNCTION lpServiceProc; // ServiceMain 指針
} SERVICE_TABLE_ENTRY;
數組以雙 NULL 結尾。StartServiceCtrlDispatcher 遍歷表,匹配服務名並創建新線程調用對應 ServiceMain。
| 狀態 | 含義 | 典型轉換 |
|---|---|---|
| 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 將強制終止進程。
配置 ChangeServiceConfig2 設置 SERVICE_CONFIG_FAILURE_ACTIONS,定義失敗時自動重啟/運行命令/重啟電腦。
Vista+ 服務無法直接顯示 UI。需用互動式服務 (已棄用) 或獨立 GUI 進程 + IPC (命名管道/RPC/共享內存) 通信。
CreateService 時指定 lpServiceStartName (服務帳戶) 和 lpPassword。推薦使用虛擬帳戶 (NT SERVICE\ServiceName) 或組管理服務帳戶 (gMSA),避免密碼管理。
HANDLE hEventLog = RegisterEventSource(nullptr, TEXT("MyDemoService"));
ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, MSG_STARTED, nullptr, 1, 0, &msg, nullptr);
DeregisterEventSource(hEventLog);
Image File Execution Options\YourService.exe\Debugger = vsjitdebugger.exe 實現服務啟動即斷點。main() 參數檢測:if (argc > 1 && _tcscmp(argv[1], TEXT("/debug")) == 0) 直接調用 ServiceMain 進入控制台調試模式。IHostedService + BackgroundService,極簡配置、依賴注入、日誌集成。