Windows 服務(Service)在系統啟動階段即自動運行,無需使用者登入,擁有獨立的會話 0 隔離環境,適合:
本文完整示範使用 Win32 API(OpenSCManager、CreateService、RegisterServiceCtrlHandler、SetServiceStatus、StartServiceCtrlDispatcher 等)建立、安裝、啟動、控制、移除 Windows 服務的標準流程。
SCM (Service Control Manager)
│
├── OpenSCManager() → 取得 SCM 控制碼
├── CreateService() → 在 SCM 資料庫註冊服務
├── OpenService() → 取得既有服務控制碼
├── ControlService() → 發送控制碼 (START/STOP/PAUSE...)
├── DeleteService() → 移除服務註冊
│
服務程式內部:
├── StartServiceCtrlDispatcher() → 進入服務主迴圈,接收 SCM 指令
├── RegisterServiceCtrlHandler() → 註冊控制處理函數 (ServiceCtrlHandler)
├── SetServiceStatus() → 回報服務狀態 (RUNNING/STOPPED/PENDING...)
└── ServiceMain() → 服務實際工作執行緒
Visual C++ 6.0:File → New → Projects → Win32 Application → 名稱 ServiceTest → 選擇「A simple application」或空白專案。
TCHAR szServiceName[] = _T("ServiceTest");
BOOL bInstall;
SERVICE_STATUS_HANDLE hServiceStatus;
SERVICE_STATUS status;
DWORD dwThreadID;
BOOL IsInstalled(); BOOL Install(); BOOL Uninstall();
void LogEvent(LPCTSTR pszFormat, ...);
void WINAPI ServiceMain();
void WINAPI ServiceCtrl(DWORD dwOpcode);
void Init() {
hServiceStatus = NULL;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
status.dwCurrentState = SERVICE_STOPPED;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP; // 關鍵:宣告接受 STOP 控制
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
}
BOOL Install() {
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!hSCM) return FALSE;
TCHAR szFilePath[MAX_PATH];
GetModuleFileName(NULL, szFilePath, MAX_PATH);
SC_HANDLE hService = CreateService(
hSCM, szServiceName, szServiceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
szFilePath, NULL, NULL, _T(""), NULL, NULL);
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return TRUE;
}
BOOL Uninstall() {
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!hSCM) return FALSE;
SC_HANDLE hService = OpenService(hSCM, szServiceName, SERVICE_STOP | DELETE);
if (hService) {
ControlService(hService, SERVICE_CONTROL_STOP, &status); // 先停止
DeleteService(hService); // 再刪除
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
return TRUE;
}
void WINAPI ServiceMain() {
// 註冊控制處理器
status.dwCurrentState = SERVICE_START_PENDING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
hServiceStatus = RegisterServiceCtrlHandler(szServiceName, ServiceCtrl);
if (!hServiceStatus) {
LogEvent(_T("Handler not installed"));
return;
}
// 回報 RUNNING 狀態
status.dwWin32ExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hServiceStatus, &status);
// ===== 服務實際工作區 =====
// 應用時將主要任務放在此處:監控、伺服器迴圈、定時器等
int i = 0;
while (i < 10) { // 示範:運行 10 秒後自動停止
Sleep(1000);
i++;
}
// ===========================
// 正常停止
status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hServiceStatus, &status);
LogEvent(_T("Service stopped"));
}
void WINAPI ServiceCtrl(DWORD dwOpcode) {
switch (dwOpcode) {
case SERVICE_CONTROL_STOP:
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(hServiceStatus, &status);
// 這裡可發送事件通知 ServiceMain 迴圈退出
break;
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus(hServiceStatus, &status);
break;
default:
break;
}
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
Init();
dwThreadID = GetCurrentThreadId();
SERVICE_TABLE_ENTRY st[] = {
{ szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }
};
if (stricmp(lpCmdLine, "/install") == 0) {
Install();
} else if (stricmp(lpCmdLine, "/uninstall") == 0) {
Uninstall();
} else {
// 作為服務啟動:進入 SCM 分派迴圈
if (!StartServiceCtrlDispatcher(st)) {
LogEvent(_T("Register Service Main Function Error!"));
}
}
return 0;
}
| 操作 | 指令 | 說明 |
|---|---|---|
| 安裝服務 | ServiceTest.exe /install | 向 SCM 註冊,啟動類型為「手動」 |
| 移除服務 | ServiceTest.exe /uninstall | 停止並刪除註冊 |
| 啟動服務 | net start ServiceTest 或 sc start ServiceTest | 透過 SCM 啟動,進入 StartServiceCtrlDispatcher |
| 停止服務 | net stop ServiceTest | 發送 STOP 控制碼,觸發 ServiceCtrl |
| 除錯技巧 | 在 ServiceMain 開頭加 DebugBreak() 或寫入日誌檔 | 服務無 UI,無法直接斷點,需靠日誌或附加偵錯器 |
SERVICE_ACCEPT_STOP 會導致 SCM 無法停止服務,顯示「無法控制」。SERVICE_RUNNING,耗時初始化放背景執行緒。ReportEvent 寫入 Windows 事件檢視器,或自行寫入旋轉日誌檔。Windows 服務開發核心在於:SCM 註冊 ↔ 狀態機回報 ↔ 控制碼處理 ↔ 工作迴圈 四位一體。掌握此流程,即可將任何長時間運行邏輯封裝為系統級服務,實現開機自啟、無人值守、高可靠性的後台運算能力。