创建Windows服务的C++指南

在多种开发场景中,服务对于应用程序的架构来说是非常有用的。本文将介绍如何在C++中创建Windows服务

C++中,Windows服务的示例并不多见。本文将使用MSDN文档作为参考,编写一个基础的Windows服务示例。

使用代码

服务至少需要以下几项:

  • 一个主入口点(像任何应用程序一样)
  • 一个服务入口点
  • 一个服务控制处理程序

可以使用Visual Studio模板项目来开始。这里创建了一个"Empty" Win32控制台应用程序。

全局变量声明

在主入口点之前,需要声明一些将在整个服务中使用的全局变量。为了更面向对象,可以创建一个代表服务的类,并使用类成员代替全局变量。为了简单起见,这里使用全局变量。

SERVICE_STATUS g_ServiceStatus = {0}; SERVICE_STATUS_HANDLE g_StatusHandle = NULL; HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;

主入口点

主入口点代码如下:

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)。任何初始化都应推迟到服务入口点。

服务入口点

服务入口点代码如下:

VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) { DWORD Status = E_FAIL; g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler); if (g_StatusHandle == NULL) { goto EXIT; } ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus)); g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwServiceSpecificExitCode = 0; g_ServiceStatus.dwCheckPoint = 0; if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) { OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); } g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (g_ServiceStopEvent == NULL) { g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = GetLastError(); g_ServiceStatus.dwCheckPoint = 1; if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) { OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); } goto EXIT; } g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 0; if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) { OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); } HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(g_ServiceStopEvent); g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 3; if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) { OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); } EXIT: return; }

服务主入口点执行以下任务:

  • 初始化在主入口点中推迟的任何必要的项目。
  • 注册服务控制处理程序,该处理程序将处理服务停止、暂停、继续、关闭等控制命令。这些通过SERVICE_STATUS结构的dwControlsAccepted字段作为位掩码进行注册。
  • 设置服务状态为SERVICE_PENDING,然后设置为SERVICE_RUNNING。在任何错误和退出时将状态设置为SERVICE_STOPPED。始终在将状态设置为SERVICE_STOPPED或SERVICE_PENDING时将SERVICE_STATUS.dwControlsAccepted设置为0。
  • 执行启动任务。例如创建线程/事件/互斥锁/IPC等。

服务控制处理程序

服务控制处理程序代码如下:

VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode) { switch (CtrlCode) { case SERVICE_CONTROL_STOP: if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) break; g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 4; if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) { OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error")); } SetEvent(g_ServiceStopEvent); break; default: break; } }

服务控制处理程序在服务主入口点中注册。每个服务必须有一个处理程序来处理来自SCM的控制请求。控制处理程序必须在30秒内返回,否则SCM将返回错误,指出服务没有响应。这是因为处理程序将在SCM的上下文中被调用,并将持有SCM直到它从处理程序返回。

只实现了并支持SERVICE_CONTROL_STOP请求。可以处理其他请求,如SERVICE_CONTROL_CONTINUE、SERVICE_CONTROL_INTERROGATE、SERVICE_CONTROL_PAUSE、SERVICE_CONTROL_SHUTDOWN等,这些可以通过Handler或HandlerEx函数注册到RegisterServiceCtrlHandler(Ex)函数。

服务工作线程

服务工作线程代码如下:

DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) { while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) { Sleep(3000); } return ERROR_SUCCESS; }

这个示例服务工作线程除了睡眠和检查服务是否收到停止控制之外什么也不做。一旦收到停止控制,服务控制处理程序将设置g_ServiceStopEvent事件。服务工作线程将中断并退出。这将向服务主程序发出信号,以返回并有效地停止服务。

安装服务

可以通过命令提示符运行以下命令来安装服务:

C:\>sc create "My Sample Service" binPath= C:\SampleService.exe

binPath=和值之间需要一个空格。同时,使用服务可执行文件的完整绝对路径。

卸载服务

C:\>sc delete "My Sample Service"
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485