3.1.3 创建Windows服务应用程序

服务应用程序 (CppWindowsService)

一、简介

本代码示例演示了如何使用 Visual C++ 创建非常基础的 Windows 服务应用程序。示例 Windows 服务将服务的开始和停止信息记录到应用程序事件日志中,并展示了如何在线程池工作线程中运行服务的主函数。你可以方便地扩展 Windows 服务框架,以满足自身的业务需求。

二、运行示例

以下步骤将进行 Windows 服务示例的演示。

步骤 1

在 Visual Studio 2008 中成功构建示例项目后,你将获得以下服务应用程序:CppWindowsService.exe。

步骤 2

以管理员身份运行命令提示符,导航到示例项目的输出文件夹,然后输入以下命令并安装服务。

CppWindowsService.exe -install

如果进程输出以下内容,说明服务已成功安装:

img

如果你未看到此输出,请在输出中查找错误代码,并调查失败原因。例如,错误代码 0x431 说明服务已经存在,你需要先将其卸载。

步骤 3

打开服务管理控制台 (services.msc)。你将在服务列表中找到“CppWindowsService 示例服务”。

img

步骤 4

在服务管理控制台中右键单击 CppWindowsService 服务,然后选择“启动”来启动服务。打开事件查看器,并导航到 Windows 日志/应用程序。你会在 CppWindowsService 中看到此事件和以下信息:

img

步骤 5

在服务管理控制台中右键单击服务,然后选择“停止”以停止服务。你会在事件查看器/Windows 日志/应用程序的 CppWindowsService 中看到此新事件和以下信息:

img

步骤 6

若要卸载此服务,请以管理员身份运行命令提示符并输入以下命令。

CppWindowsService.exe -remove

如果此服务已成功删除,你将看到以下输出:

img

三、使用代码

步骤 1

在 Visual Studio 2008 中添加名为 CppWindowsService 的新 Visual C++ / Win32 / Win32 控制台应用程序项目。在 Win32 应用程序向导的“应用程序设置”中取消选择“预译编头”选项,并在创建项目后删除 stdafx.h、stdafx.cpp 和 targetver.h 文件。

步骤 2

在 CppWindowsService.cpp 中定义服务设置。

  1. // Internal name of the service
  2. #define SERVICE_NAME L"CppWindowsService"
  3. // Displayed name of the service
  4. #define SERVICE_DISPLAY_NAME L"CppWindowsService Sample Service"
  5. // Service start options.
  6. #define SERVICE_START_TYPE SERVICE_DEMAND_START
  7. // List of service dependencies - "dep1\0dep2\0\0"
  8. #define SERVICE_DEPENDENCIES L""
  9. // The name of the account under which the service should run
  10. #define SERVICE_ACCOUNT L"NT AUTHORITY\\LocalService"
  11. // The password to the service account name
  12. #define SERVICE_PASSWORD NULL

安全说明:在本示例代码中,服务配置为以 LocalService(而非 LocalSystem)身份运行。LocalSystem 帐户有广泛的权限。请小心使用 LocalSystem 帐户,因为这可能会增加你受到恶意软件攻击的风险。对于不需要广泛权限的任务,请考虑使用 LocalService 帐户,它会在本地计算机上扮演非特权用户的角色,并对任何远程服务器显示匿名凭据。

步骤 3

将 CppWindowsService.cpp 中的应用程序入口点(主要)替换为以下代码。根据命令行中的参数,该函数会通过调用将在后续步骤中声明并实施的多个例程来安装、卸载或启动服务。

  1. int wmain(int argc, wchar_t *argv[])
  2. {
  3. if ((argc > 1) && ((*argv[1] == L'-' || (*argv[1] == L'/'))))
  4. {
  5. if (_wcsicmp(L"install", argv[1] + 1) == 0)
  6. {
  7. // Install the service when the command is
  8. // "-install" or "/install".
  9. InstallService(
  10. SERVICE_NAME, // Name of service
  11. SERVICE_DISPLAY_NAME, // Name to display
  12. SERVICE_START_TYPE, // Service start type
  13. SERVICE_DEPENDENCIES, // Dependencies
  14. SERVICE_ACCOUNT, // Service running account
  15. SERVICE_PASSWORD // Password of the account
  16. );
  17. }
  18. else if (_wcsicmp(L"remove", argv[1] + 1) == 0)
  19. {
  20. // Uninstall the service when the command is
  21. // "-remove" or "/remove".
  22. UninstallService(SERVICE_NAME);
  23. }
  24. }
  25. else
  26. {
  27. wprintf(L"Parameters:\n");
  28. wprintf(L" -install to install the service.\n");
  29. wprintf(L" -remove to remove the service.\n");
  30. CSampleService service(SERVICE_NAME);
  31. if (!CServiceBase::Run(service))
  32. {
  33. wprintf(L"Service failed to run w/err 0x%08lx\n", GetLastError());
  34. }
  35. }
  36. return 0;
  37. }

步骤 4

添加 ServiceBase.h 和 ServiceBase.cpp 文件,为将作为服务应用程序一部分存在的服务提供基类。该类名为 “CServiceBase”。创建新服务类时必须从该类派生。

该服务基类有以下公用函数:

  1. // It register the executable for a service with SCM.
  2. static BOOL CServiceBase::Run(CServiceBase &service)
  3. // This is the constructor of the service class. The optional parameters
  4. // (fCanStop, fCanShutdown and fCanPauseContinue) allow you to specify
  5. // whether the service can be stopped, paused and continued, or be
  6. // notified when system shutdown occurs.
  7. CServiceBase::CServiceBase(PWSTR pszServiceName,
  8. BOOL fCanStop = TRUE,
  9. BOOL fCanShutdown = TRUE,
  10. BOOL fCanPauseContinue = FALSE)
  11. // This is the virtual destructor of the service class.
  12. virtual ~CServiceBase::CServiceBase(void);
  13. // Funtion that stops the service.
  14. void CServiceBase::Stop();

该类还提供了以下虚拟成员函数。你可以在派生类中实施这些函数。这些函数将在服务启动、停止、暂停、继续和系统关闭时执行。

  1. virtual void OnStart(DWORD dwArgc, PWSTR *pszArgv);
  2. virtual void OnStop();
  3. virtual void OnPause();
  4. virtual void OnContinue();
  5. virtual void OnShutdown();

步骤 5

添加 SampleService.h 和 SampleService.cpp 文件,提供派生自服务基类 (CServiceBase) 的示例服务类。示例服务将服务的开始和停止信息记录到应用程序日志中,并展示了如何在线程池工作线程中运行服务的主函数。

在服务启动时执行的 CSampleService::OnStart 将调用 CServiceBase::WriteEventLogEntry 来记录服务启动信息。它还将调用 CThreadPool::QueueUserWorkItem,以便将在工作线程中执行的主服务函数 (CSampleService::ServiceWorkerThread) 排队。

注意:服务应用程序设计为长时间运行。因此,它通常可轮询或监视系统中的某些内容。已在 OnStart 方法中设置了监视。但是,OnStart 不会实际进行监视。服务操作开始之后,OnStart 方法必须返回操作系统。它不能始终循环或阻止。若要设置简单监测机制,常规解决方案是在 OnStart 中创建一个计时器。随后计时器将定期引发代码中的事件,在此时间服务可进行监测。另一种解决方案是生成一个新

线程来执行主服务函数,此过程将在本代码示例中进行演示。

  1. void CSampleService::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
  2. {
  3. // Log a service start message to the Application log.
  4. WriteEventLogEntry(L"CppWindowsService in OnStart",
  5. EVENTLOG_INFORMATION_TYPE);
  6. // Queue the main service function for execution in a worker thread.
  7. CThreadPool::QueueUserWorkItem(&CSampleService::ServiceWorkerThread, this);
  8. }

在服务停止时执行的 CSampleService::OnStop 将调用 CServiceBase::WriteEventLogEntry 来记录服务停止信息。接下来,它会将成员变量 m_fStopping 设置为 TRUE,以指示服务正在停止,并等待由 m_hStoppedEvent 事件对象用信号通知的主服务函数完成。

C++

  1. void CSampleService::OnStop()
  2. {
  3. WriteEventLogEntry(L"CppWindowsService in OnStop",
  4. EVENTLOG_INFORMATION_TYPE);
  5. // Indicate that the service is stopping and wait for the finish of the
  6. // main service function (ServiceWorkerThread).
  7. m_fStopping = TRUE;
  8. if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
  9. {
  10. throw GetLastError();
  11. }
  12. }

CSampleService::ServiceWorkerThread 在线程池工作线程中运行。它将执行服务的主函数,例如,通过命名管道与客户端应用程序进行通信。为了使主函数正常完成,当服务即将停止时,应定期检查 m_fStopping 变量。当此函数检测到服务正在停止时,将清除该工作并用信号通知 m_hStoppedEvent 事件对象。

  1. void CSampleService::ServiceWorkerThread(void)
  2. {
  3. // Periodically check if the service is stopping.
  4. while (!m_fStopping)
  5. {
  6. // Perform main service function here...
  7. ::Sleep(2000); // Simulate some lengthy operations.
  8. }
  9. // Signal the stopped event.
  10. SetEvent(m_hStoppedEvent);
  11. }

步骤 6

添加 ServiceInstaller.h 和 ServiceInstaller.cpp 文件,声明并实施安装和卸载此服务的函数:

  1. InstallService Installs the service
  2. UninstallService Uninstalls the service

原文及源码链接