3.1.3 创建Windows服务应用程序
服务应用程序 (CppWindowsService)
一、简介
本代码示例演示了如何使用 Visual C++ 创建非常基础的 Windows 服务应用程序。示例 Windows 服务将服务的开始和停止信息记录到应用程序事件日志中,并展示了如何在线程池工作线程中运行服务的主函数。你可以方便地扩展 Windows 服务框架,以满足自身的业务需求。
二、运行示例
以下步骤将进行 Windows 服务示例的演示。
步骤 1
在 Visual Studio 2008 中成功构建示例项目后,你将获得以下服务应用程序:CppWindowsService.exe。
步骤 2
以管理员身份运行命令提示符,导航到示例项目的输出文件夹,然后输入以下命令并安装服务。
CppWindowsService.exe -install
如果进程输出以下内容,说明服务已成功安装:
如果你未看到此输出,请在输出中查找错误代码,并调查失败原因。例如,错误代码 0x431 说明服务已经存在,你需要先将其卸载。
步骤 3
打开服务管理控制台 (services.msc)。你将在服务列表中找到“CppWindowsService 示例服务”。
步骤 4
在服务管理控制台中右键单击 CppWindowsService 服务,然后选择“启动”来启动服务。打开事件查看器,并导航到 Windows 日志/应用程序。你会在 CppWindowsService 中看到此事件和以下信息:
步骤 5
在服务管理控制台中右键单击服务,然后选择“停止”以停止服务。你会在事件查看器/Windows 日志/应用程序的 CppWindowsService 中看到此新事件和以下信息:
步骤 6
若要卸载此服务,请以管理员身份运行命令提示符并输入以下命令。
CppWindowsService.exe -remove
如果此服务已成功删除,你将看到以下输出:
三、使用代码
步骤 1
在 Visual Studio 2008 中添加名为 CppWindowsService 的新 Visual C++ / Win32 / Win32 控制台应用程序项目。在 Win32 应用程序向导的“应用程序设置”中取消选择“预译编头”选项,并在创建项目后删除 stdafx.h、stdafx.cpp 和 targetver.h 文件。
步骤 2
在 CppWindowsService.cpp 中定义服务设置。
// Internal name of the service
#define SERVICE_NAME L"CppWindowsService"
// Displayed name of the service
#define SERVICE_DISPLAY_NAME L"CppWindowsService Sample Service"
// Service start options.
#define SERVICE_START_TYPE SERVICE_DEMAND_START
// List of service dependencies - "dep1\0dep2\0\0"
#define SERVICE_DEPENDENCIES L""
// The name of the account under which the service should run
#define SERVICE_ACCOUNT L"NT AUTHORITY\\LocalService"
// The password to the service account name
#define SERVICE_PASSWORD NULL
安全说明:在本示例代码中,服务配置为以 LocalService(而非 LocalSystem)身份运行。LocalSystem 帐户有广泛的权限。请小心使用 LocalSystem 帐户,因为这可能会增加你受到恶意软件攻击的风险。对于不需要广泛权限的任务,请考虑使用 LocalService 帐户,它会在本地计算机上扮演非特权用户的角色,并对任何远程服务器显示匿名凭据。
步骤 3
将 CppWindowsService.cpp 中的应用程序入口点(主要)替换为以下代码。根据命令行中的参数,该函数会通过调用将在后续步骤中声明并实施的多个例程来安装、卸载或启动服务。
int wmain(int argc, wchar_t *argv[])
{
if ((argc > 1) && ((*argv[1] == L'-' || (*argv[1] == L'/'))))
{
if (_wcsicmp(L"install", argv[1] + 1) == 0)
{
// Install the service when the command is
// "-install" or "/install".
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
}
else if (_wcsicmp(L"remove", argv[1] + 1) == 0)
{
// Uninstall the service when the command is
// "-remove" or "/remove".
UninstallService(SERVICE_NAME);
}
}
else
{
wprintf(L"Parameters:\n");
wprintf(L" -install to install the service.\n");
wprintf(L" -remove to remove the service.\n");
CSampleService service(SERVICE_NAME);
if (!CServiceBase::Run(service))
{
wprintf(L"Service failed to run w/err 0x%08lx\n", GetLastError());
}
}
return 0;
}
步骤 4
添加 ServiceBase.h 和 ServiceBase.cpp 文件,为将作为服务应用程序一部分存在的服务提供基类。该类名为 "CServiceBase"。创建新服务类时必须从该类派生。
该服务基类有以下公用函数:
// It register the executable for a service with SCM.
static BOOL CServiceBase::Run(CServiceBase &service)
// This is the constructor of the service class. The optional parameters
// (fCanStop, fCanShutdown and fCanPauseContinue) allow you to specify
// whether the service can be stopped, paused and continued, or be
// notified when system shutdown occurs.
CServiceBase::CServiceBase(PWSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE)
// This is the virtual destructor of the service class.
virtual ~CServiceBase::CServiceBase(void);
// Funtion that stops the service.
void CServiceBase::Stop();
该类还提供了以下虚拟成员函数。你可以在派生类中实施这些函数。这些函数将在服务启动、停止、暂停、继续和系统关闭时执行。
virtual void OnStart(DWORD dwArgc, PWSTR *pszArgv);
virtual void OnStop();
virtual void OnPause();
virtual void OnContinue();
virtual void OnShutdown();
步骤 5
添加 SampleService.h 和 SampleService.cpp 文件,提供派生自服务基类 (CServiceBase) 的示例服务类。示例服务将服务的开始和停止信息记录到应用程序日志中,并展示了如何在线程池工作线程中运行服务的主函数。
在服务启动时执行的 CSampleService::OnStart 将调用 CServiceBase::WriteEventLogEntry 来记录服务启动信息。它还将调用 CThreadPool::QueueUserWorkItem,以便将在工作线程中执行的主服务函数 (CSampleService::ServiceWorkerThread) 排队。
注意:服务应用程序设计为长时间运行。因此,它通常可轮询或监视系统中的某些内容。已在 OnStart 方法中设置了监视。但是,OnStart 不会实际进行监视。服务操作开始之后,OnStart 方法必须返回操作系统。它不能始终循环或阻止。若要设置简单监测机制,常规解决方案是在 OnStart 中创建一个计时器。随后计时器将定期引发代码中的事件,在此时间服务可进行监测。另一种解决方案是生成一个新
线程来执行主服务函数,此过程将在本代码示例中进行演示。
void CSampleService::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
{
// Log a service start message to the Application log.
WriteEventLogEntry(L"CppWindowsService in OnStart",
EVENTLOG_INFORMATION_TYPE);
// Queue the main service function for execution in a worker thread.
CThreadPool::QueueUserWorkItem(&CSampleService::ServiceWorkerThread, this);
}
在服务停止时执行的 CSampleService::OnStop 将调用 CServiceBase::WriteEventLogEntry 来记录服务停止信息。接下来,它会将成员变量 m_fStopping 设置为 TRUE,以指示服务正在停止,并等待由 m_hStoppedEvent 事件对象用信号通知的主服务函数完成。
C++
void CSampleService::OnStop()
{
WriteEventLogEntry(L"CppWindowsService in OnStop",
EVENTLOG_INFORMATION_TYPE);
// Indicate that the service is stopping and wait for the finish of the
// main service function (ServiceWorkerThread).
m_fStopping = TRUE;
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
{
throw GetLastError();
}
}
CSampleService::ServiceWorkerThread 在线程池工作线程中运行。它将执行服务的主函数,例如,通过命名管道与客户端应用程序进行通信。为了使主函数正常完成,当服务即将停止时,应定期检查 m_fStopping 变量。当此函数检测到服务正在停止时,将清除该工作并用信号通知 m_hStoppedEvent 事件对象。
void CSampleService::ServiceWorkerThread(void)
{
// Periodically check if the service is stopping.
while (!m_fStopping)
{
// Perform main service function here...
::Sleep(2000); // Simulate some lengthy operations.
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
步骤 6
添加 ServiceInstaller.h 和 ServiceInstaller.cpp 文件,声明并实施安装和卸载此服务的函数:
InstallService Installs the service
UninstallService Uninstalls the service