本文是来自CodeProject中的一篇名为Simple Windows Service in C++的译文,原文地址为:https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus,作者为:Mohit Arora。
这是一篇使用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.cpp
介绍
这篇文章展示如何使用C++创建一个基本的Windows服务程序。根据应用程序的体系结构,服务在许多开发方案中非常有用。
背景
我在C++中找到的Windows服务示例并不多。我使用MSDN编写这个非常基本的Windows服务。
使用代码
(1)主入口点(与任何应用程序一样)
(2)服务入口点
(3)服务控制处理程序
你可以使用Visual Studio模板项目来帮助你入门。我刚创建了一个空的Win32控制台应用程序。
在我们开始主入口程序点之前,我们需要声明一些将在整个服务中使用的全局变量。为了更加面向对象,你始终可以创建一个表示服务的类,并使用类成员代表全局变量。为了简单起见,我将使用全局变量。
我们需要一个SERVICE_STATUS结构体,将用于向Windows服务控制管理器(SCM)报告服务的状态。
SERVICE_STATUS g_ServiceStatus = {0};
我们同样需要一个SERVICE_STATUS_HANDLE句柄,用于在SCM注册后引用我们的Windows服务实例。
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
下面是一些额外的全局变量和函数声明,随着我们的继续将被使用和解释。
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日:根据用户评论更新了有关如何安装服务的详细信息。