在多种开发场景中,服务对于应用程序的架构来说是非常有用的。本文将介绍如何在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;
}
服务主入口点执行以下任务:
服务控制处理程序代码如下:
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"