本文是來自CodeProject中的一篇名爲Simple Windows Service in C++的譯文,原文地址爲:https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus,做者爲:Mohit Arora。git
這是一篇使用C++展現如何建立簡單的Windows服務的文章。
源代碼下載地址爲:https://www.codeproject.com/KB/system/499465/SampleService.zip或者https://github.com/ccf19881030/Cplus_libs_wrapper/blob/master/sources/Windows%20VC++%E7%BC%96%E7%A8%8B/VC++%E7%BC%96%E5%86%99Windows%E6%9C%8D%E5%8A%A1%E7%A8%8B%E5%BA%8F/SampleServiceMain.cppgithub
介紹
這篇文章展現如何使用C++建立一個基本的Windows服務程序。根據應用程序的體系結構,服務在許多開發方案中很是有用。app
背景
我在C++中找到的Windows服務示例並很少。我使用MSDN編寫這個很是基本的Windows服務。函數
使用代碼
(1)主入口點(與任何應用程序同樣)
(2)服務入口點
(3)服務控制處理程序
你可使用Visual Studio模板項目來幫助你入門。我剛建立了一個空的Win32控制檯應用程序。spa
在咱們開始主入口程序點以前,咱們須要聲明一些將在整個服務中使用的全局變量。爲了更加面向對象,你始終能夠建立一個表示服務的類,並使用類成員表明全局變量。爲了簡單起見,我將使用全局變量。命令行
咱們須要一個SERVICE_STATUS結構體,將用於向Windows服務控制管理器(SCM)報告服務的狀態。線程
SERVICE_STATUS g_ServiceStatus = {0};
咱們一樣須要一個SERVICE_STATUS_HANDLE句柄,用於在SCM註冊後引用咱們的Windows服務實例。code
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
下面是一些額外的全局變量和函數聲明,隨着咱們的繼續將被使用和解釋。orm
SERVICE_STATUS g_ServiceStatus = {0}; SERVICE_STATUS_HANDLE g_StatusHandle = NULL; HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE; VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv); VOID WINAPI ServiceCtrlHandler (DWORD); DWORD WINAPI ServiceWorkerThread (LPVOID lpParam); #define SERVICE_NAME _T("My Sample Service")
主函數入口對象
int _tmain (int argc, TCHAR *argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher (ServiceTable) == FALSE)
{
return GetLastError ();
}
return 0;
}
在主函數入口點中,你能夠快速調用StartServiceCtrlDispatcher,以便SCM能夠調用你的服務入口點(上例中的ServiceMain)。 你但願將任何初始化推遲到你接下來定義的服務入口點。
1 VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv) 2 3 { 4 5 DWORD Status = E_FAIL; 6 7 8 9 // Register our service control handler with the SCM 10 11 g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler); 12 13 14 15 if (g_StatusHandle == NULL) 16 17 { 18 19 goto EXIT; 20 21 } 22 23 24 25 // Tell the service controller we are starting 26 27 ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus)); 28 29 g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 30 31 g_ServiceStatus.dwControlsAccepted = 0; 32 33 g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 34 35 g_ServiceStatus.dwWin32ExitCode = 0; 36 37 g_ServiceStatus.dwServiceSpecificExitCode = 0; 38 39 g_ServiceStatus.dwCheckPoint = 0; 40 41 42 43 if (SetServiceStatus (g_StatusHandle , &g_ServiceStatus) == FALSE) 44 45 { 46 47 OutputDebugString(_T( 48 49 "My Sample Service: ServiceMain: SetServiceStatus returned error")); 50 51 } 52 53 54 55 /* 56 57 * Perform tasks necessary to start the service here 58 59 */ 60 61 62 63 // Create a service stop event to wait on later 64 65 g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL); 66 67 if (g_ServiceStopEvent == NULL) 68 69 { 70 71 // Error creating event 72 73 // Tell service controller we are stopped and exit 74 75 g_ServiceStatus.dwControlsAccepted = 0; 76 77 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 78 79 g_ServiceStatus.dwWin32ExitCode = GetLastError(); 80 81 g_ServiceStatus.dwCheckPoint = 1; 82 83 84 85 if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) 86 87 { 88 89 OutputDebugString(_T( 90 91 "My Sample Service: ServiceMain: SetServiceStatus returned error")); 92 93 } 94 95 goto EXIT; 96 97 } 98 99 100 101 // Tell the service controller we are started 102 103 g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 104 105 g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; 106 107 g_ServiceStatus.dwWin32ExitCode = 0; 108 109 g_ServiceStatus.dwCheckPoint = 0; 110 111 112 113 if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) 114 115 { 116 117 OutputDebugString(_T( 118 119 "My Sample Service: ServiceMain: SetServiceStatus returned error")); 120 121 } 122 123 124 125 // Start a thread that will perform the main task of the service 126 127 HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL); 128 129 130 131 // Wait until our worker thread exits signaling that the service needs to stop 132 133 WaitForSingleObject (hThread, INFINITE); 134 135 136 137 138 139 /* 140 141 * Perform any cleanup tasks 142 143 */ 144 145 146 147 CloseHandle (g_ServiceStopEvent); 148 149 150 151 // Tell the service controller we are stopped 152 153 g_ServiceStatus.dwControlsAccepted = 0; 154 155 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 156 157 g_ServiceStatus.dwWin32ExitCode = 0; 158 159 g_ServiceStatus.dwCheckPoint = 3; 160 161 162 163 if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) 164 165 { 166 167 OutputDebugString(_T( 168 169 "My Sample Service: ServiceMain: SetServiceStatus returned error")); 170 171 } 172 173 174 175 EXIT: 176 177 return; 178 179 }
服務主入口點執行如下任務:
(1)初始化咱們從主函數入口推遲的任何須要項目。
(2)註冊服務控制處理程序,它將處理服務Stop,Pause,Continue,Shutdown**等控制命令。 這些是經過SERVICE_STATUS結構的dwControlsAccepted字段做爲位掩碼註冊的。
(3)將服務狀態設置爲SERVICE_PENDING,而後設置爲SERVICE_RUNNING。 在任何錯誤和退出時將狀態設置爲SERVICE_STOPPED。 當狀態設置爲SERVICE_STOPPED或SERVICE_PENDING時,始終將SERVICE_STATUS.dwControlsAccepted設置爲0。
(4)執行啓動任務。向建立線程/事件/互斥量/IPCs/等。
服務控制處理程序
1 VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode) 2 { 3 switch (CtrlCode) 4 { 5 case SERVICE_CONTROL_STOP : 6 7 if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) 8 break; 9 10 /* 11 * Perform tasks necessary to stop the service here 12 */ 13 14 g_ServiceStatus.dwControlsAccepted = 0; 15 g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; 16 g_ServiceStatus.dwWin32ExitCode = 0; 17 g_ServiceStatus.dwCheckPoint = 4; 18 19 if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) 20 { 21 OutputDebugString(_T( 22 "My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error")); 23 } 24 25 // This will signal the worker thread to start shutting down 26 SetEvent (g_ServiceStopEvent); 27 28 break; 29 30 default: 31 break; 32 } 33 }
服務控制處理程序已在你的服務主入口點註冊。 每一個服務都必須有一個處理程序來處理來自SCM的控制請求。 控制處理程序必須在30秒內返回,不然SCM將返回錯誤,該錯誤指出服務沒有響應。 這是由於處理程序將在SCM的上下文中調用,並將保持SCM直到它從處理程序返回。
我只實現並支持SERVICE_CONTROL_STOP請求。 你能夠處理其餘請求,例如SERVICE_CONTROL_CONTINUE,SERVICE_CONTROL_INTERROGATE,SERVICE_CONTROL_PAUSE,SERVICE_CONTROL_SHUTDOWN以及可使用RegisterServiceCtrlHandler(Ex)函數註冊的Handler或HandlerEx函數支持的其餘請求。
服務工做線程
1 DWORD WINAPI ServiceWorkerThread (LPVOID lpParam) 2 { 3 // Periodically check if the service has been requested to stop 4 while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) 5 { 6 /* 7 * Perform main service function here 8 */ 9 10 // Simulate some work by sleeping 11 Sleep(3000); 12 } 13 14 return ERROR_SUCCESS; 15 }
此示例服務工做線程除了休眠以外什麼都不作,並檢查服務是否已收到要中止的控制。 一旦收到中止服務的控制信息,服務控制處理程序就會設置g_ServiceStopEvent事件。 接着服務工做者線程斷開並退出。 這將通知Service Main例程返回並有效地中止服務。
安裝服務
你能夠經過在命令行提示符中運行一下命令來安裝服務(**注意要以管理員身份運行**):
C:\>sc create "My Sample Service" binPath= C:\SampleService.exe
在binPath=和值[?]之間須要一個空格。此外,要使用服務可執行程序的絕對路徑。
你如今應該在Windows服務控制檯中看到該服務。 從這裏你能夠開始和中止服務。
## 卸載服務
你能夠從命令提示符經過運行如下命令卸載服務:
C:\>sc delete "My Sample Service"
歷史
11/28/2012:文章和代碼的初始版本。11/29/2012:改進了代碼並修復了文章示例代碼中的一個拼寫錯誤。2015年11月11日:根據用戶評論更新了有關如何安裝服務的詳細信息。