Simple Windows Service in C++

     本文是來自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日:根據用戶評論更新了有關如何安裝服務的詳細信息。

相關文章
相關標籤/搜索