前言
以前做项目需要用到Windows服务,于是随便在网上找了一篇C++版的Windows服务代码,封装的很好,直接拿来用就可以。现在项目需求发生了一点小变化,需要服务时刻监听启动的exe是否异常,如果异常就重新启动。于是研究下代码,顺便熟悉下Windows服务。
文件构成
整个文件有两部分构成
- CServiceBase类
- ServiceInstall文件,包含安装、卸载、更新服务描述
程序入口处理
因为我们最终需要打包自动安装,卸载程序的时候自动卸载服务,所以在main函数中可以通过参数判断,程序是安装服务,还是卸载服务或者是运行服务。
// 服务启动类型
#define SERVICE_START_TYPE SERVICE_AUTO_START
// 服务依赖项
#define SERVICE_DEPENDENCIES L""
// 服务配置为以 LocalSystem(而非 LocalService)身份运行
#define SERVICE_ACCOUNT NULL //L"NT AUTHORITY\\LocalService"
// The password to the service account name
#define SERVICE_PASSWORD NULL
int _tmain(int argc, TCHAR* argv[])
{
if ((argc > 1) && ((*argv[1] == L'-' || (*argv[1] == L'/'))))
{
if (_wcsicmp(L"install", argv[1] + 1) == 0)
{
//通过命令行的方式注册服务
InstallService(
SERVICE_NAME, // 服务的名称,不用多说,一般为sc使用的名称或net使用的名称
SERVICE_DISPLAY_NAME, // 服务显示的名称,显示给用户的名称
/*
#define SERVICE_BOOT_START 0x00000000 //系统加载程序加载驱动启动,仅对驱动程序有效
#define SERVICE_SYSTEM_START 0x00000001 //由 IOInitSystem 函数启动的设备驱动程序
#define SERVICE_AUTO_START 0x00000002 //操作系统启动的时候启动
#define SERVICE_DEMAND_START 0x00000003 //手动启动
#define SERVICE_DISABLED 0x00000004 //服务已被禁用
// 服务启动类型,分为 以上五种,一般为SERVICE_AUTO_START,
// 0和1针对于驱动程序,一般操作系统启动不起来大部分原因是驱动程序加载不上
*/
SERVICE_START_TYPE,
SERVICE_DEPENDENCIES, // 依赖的服务,一般没有依赖,当然如果你依赖另一个服务要填它的SERVICE_NAME
// 服务运行的帐号,有四个选项
// 1. 当前登录账号,一般我们是不知道用户电脑的账号的,而且也不可能统一账号和密码
// 2. LocalService,计算机上非特权用户
// 3. LocalSystem,服务控制管理员使用的帐户
// 4. NetworkService,提供广泛的本地特权的帐户,该帐户将计算机的凭据提供给所有远程服务器
// 如果此参数为NULL,则 CreateService使用 LocalSystem帐户
// NT AUTHORITY\LocalService 为LocalService账号
// NT AUTHORITY\NetworkService 为NetworkService账号
// 具体请看参考文献2、3、4和5
SERVICE_ACCOUNT,
SERVICE_PASSWORD // 帐号的密码,一般填NULL
);
//更新服务描述
updateServiceDescription(SERVICE_NAME,SERVICE_DESC);
}
else if (_wcsicmp(L"remove", argv[1] + 1) == 0)
{
//通过命令行的方式卸载服务
// "-remove" or "/remove".
UninstallService(SERVICE_NAME);
}
}
else
{
CImpCertService service(SERVICE_NAME);
if (!CServiceBase::Run(service))
{
WriteToLog("服务启动失败,错误码:"+GetLastError());
}
}
return 0;
}
这样打包的时候就可以使用命令参数来进行安装或卸载。
比如编译出来是 YYService.exe,我用的是inno setup打包,脚本语言就可以写成
[Run]
Filename:"{app}/YYService.exe"; Parameters:"-install";Flags:runhidden;StatusMsg:"正在安装服务"
Filename: "net.exe"; Parameters: "start YYService"; Flags:runhidden;StatusMsg:"正在启动服务"
[UninstallRun]
Filename:"net.exe"; Parameters:"stop YYService";Flags:runhidden waituntilterminated;
Filename:"{app}/YYService.exe"; Parameters:"-remove";Flags:runhidden waituntilterminated;
服务启动与停止
我们首先看服务怎么运行的。
#pragma once
#include <windows.h>
class CServiceBase
{
public:
// 向SCM注册服务的可执行程序文件,调用Run方法后,SCM后发出Start命令,这将调用服务中的OnStart方法
static BOOL Run(CServiceBase &service);
// 构造函数,允许你指定服务可以停止、暂停和继续
CServiceBase(PWSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
// 服务析构函数
virtual ~CServiceBase(void);
// 停止服务
void Stop();
protected:
// 在派生类中实现,当调用Run时,会使SCM调用OnStart
virtual void OnStart(DWORD dwArgc, PWSTR *pszArgv);
// 在派生类中实现,当服务停止时SCM会调用该方法
virtual void OnStop();
// 在派生类中实现,当服务暂停时,SCM会调用该方法
virtual void OnPause();
// 在派生类中实现,当服务继续时,SCM会调用该方法
virtual void OnContinue();
// 当系统关机时,会调用该方法
virtual void OnShutdown();
// 设置服务状态,并将状态报告给SCM
void SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode = NO_ERROR,
DWORD dwWaitHint = 0);
// 将日志信息写入Windows日志中
void WriteEventLogEntry(PWSTR pszMessage, WORD wType);
// 将错误信息写入到Windows日志中
void WriteErrorLogEntry(PWSTR pszFunction,
DWORD dwError = GetLastError());
private:
// 服务的入口点. 注册并启动服务
static void WINAPI ServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
// 这个函数将会在SCM发送控制码给这个服务的时候调用
static void WINAPI ServiceCtrlHandler(DWORD dwCtrl);
// 启动服务
void Start(DWORD dwArgc, PWSTR *pszArgv);
// 暂停服务
void Pause();
// 继续服务
void Continue();
// 当系统关机时执行
void Shutdown();
// 子类对象
static CServiceBase *s_service;
// 服务名称
PWSTR m_name;
// 服务状态
SERVICE_STATUS m_status;
// 服务状态句柄
SERVICE_STATUS_HANDLE m_statusHandle;
};
#pragma region Includes
#include "ServiceBase.h"
#include <assert.h>
#include <strsafe.h>
#pragma endregion
#pragma region Static Members
// 初始化
CServiceBase *CServiceBase::s_service = NULL;
// 目的:向SCM注册服务的可执行文件,在调用这个方法之后,SCM会发出Start命令从而调用服务的OnStart方法
BOOL CServiceBase::Run(CServiceBase &service)
{
s_service = &service;
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ service.m_name, ServiceMain },
{ NULL, NULL }
};
// 将服务的主线程连接到SCM。
// StartServiceCtrlDispatcher时,程序由服务控制管理器接管
// SCM 根据需要启动的服务名称,在传入的数组指针中,找到对应的入口函数,然后调用它
// 当对应的入口函数返回时结束服务,并将后续代码的控制权转交给对应主进程,由主进程接着执行后面的代码
// 注意:此处有时间要求,如果时间超过30秒,就应该另起子线程进行初始化
return StartServiceCtrlDispatcher(serviceTable);
}
// 服务启动的入口点,如果学过Windows驱动就很容易理解这个概念
// 注册服务函数,当SCM发送控制码时,会调用注册的对应函数
void WINAPI CServiceBase::ServiceMain(DWORD dwArgc, PWSTR *pszArgv)
{
assert(s_service != NULL);
// 注册服务函数
s_service->m_statusHandle = RegisterServiceCtrlHandler(
s_service->m_name, ServiceCtrlHandler);
if (s_service->m_statusHandle == NULL)
{
throw GetLastError();
}
// Start the service.
s_service->Start(dwArgc, pszArgv);
}
// PURPOSE: 注册控制码对应的函数
//
// PARAMETERS:
// * dwCtrlCode - 控制码
//
// SERVICE_CONTROL_CONTINUE
// SERVICE_CONTROL_INTERROGATE
// SERVICE_CONTROL_NETBINDADD
// SERVICE_CONTROL_NETBINDDISABLE
// SERVICE_CONTROL_NETBINDREMOVE
// SERVICE_CONTROL_PARAMCHANGE
// SERVICE_CONTROL_PAUSE
// SERVICE_CONTROL_SHUTDOWN
// SERVICE_CONTROL_STOP
//
void WINAPI CServiceBase::ServiceCtrlHandler(DWORD dwCtrl)
{
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
case SERVICE_CONTROL_INTERROGATE: break;
default: break;
}
}
#pragma endregion
#pragma region Service Constructor and Destructor
//
//
// PARAMETERS:
// * pszServiceName - 服务名称
// * fCanStop - 服务是否可用暂停
// * fCanShutdown - 当关机的时候是否会通知服务
// * fCanPauseContinue - 服务是否可以继续
//
CServiceBase::CServiceBase(PWSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
{
// Service name must be a valid string and cannot be NULL.
m_name = (pszServiceName == NULL) ? L"" : pszServiceName;
m_statusHandle = NULL;
// The service runs in its own process.
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
// The service is starting.
m_status.dwCurrentState = SERVICE_START_PENDING;
// The accepted commands of the service.
DWORD dwControlsAccepted = 0;
if (fCanStop)
dwControlsAccepted |= SERVICE_ACCEPT_STOP;
if (fCanShutdown)
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
if (fCanPauseContinue)
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
m_status.dwControlsAccepted = dwControlsAccepted;
m_status.dwWin32ExitCode = NO_ERROR;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
}
//
// FUNCTION: CServiceBase::~CServiceBase()
//
// PURPOSE: The virtual destructor of CServiceBase.
//
CServiceBase::~CServiceBase(void)
{
}
#pragma endregion
#pragma region Service Start, Stop, Pause, Continue, and Shutdown
//
// 启动服务
//
// PURPOSE: The function starts the service. It calls the OnStart virtual
// function in which you can specify the actions to take when the service
// starts. If an error occurs during the startup, the error will be logged
// in the Application event log, and the service will be stopped.
//
// PARAMETERS:
// * dwArgc - number of command line arguments
// * lpszArgv - array of command line arguments
//
void CServiceBase::Start(DWORD dwArgc, PWSTR *pszArgv)
{
try
{
//告诉SCM启动中
SetServiceStatus(SERVICE_START_PENDING);
// 调用子类的启动函数
OnStart(dwArgc, pszArgv);
//告诉SCM启动了
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Start", dwError);
// 设置服务停止
SetServiceStatus(SERVICE_STOPPED, dwError);
}
catch (...)
{
// 写错误日志
WriteEventLogEntry(L"Service failed to start.", EVENTLOG_ERROR_TYPE);
// 设置服务停止
SetServiceStatus(SERVICE_STOPPED);
}
}
// 留着子类实现,默认不做任何事情
void CServiceBase::OnStart(DWORD dwArgc, PWSTR *pszArgv)
{
}
//
// FUNCTION: CServiceBase::Stop()
//
// PURPOSE: The function stops the service. It calls the OnStop virtual
// function in which you can specify the actions to take when the service
// stops. If an error occurs, the error will be logged in the Application
// event log, and the service will be restored to the original state.
//
void CServiceBase::Stop()
{
DWORD dwOriginalState = m_status.dwCurrentState;
try
{
// 告诉SCM 停止服务中
SetServiceStatus(SERVICE_STOP_PENDING);
// 子类停止服务做得事情
OnStop();
// 告诉SCM已经停止
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
// 写错误日志
WriteErrorLogEntry(L"Service Stop", dwError);
// 设置服务状态
SetServiceStatus(dwOriginalState);
}
catch (...)
{
// 写错误日志
WriteEventLogEntry(L"Service failed to stop.", EVENTLOG_ERROR_TYPE);
// 设置服务状态
SetServiceStatus(dwOriginalState);
}
}
//
// 默认不做任何事情
void CServiceBase::OnStop()
{
}
//
// FUNCTION: CServiceBase::Pause()
//
// PURPOSE: The function pauses the service if the service supports pause
// and continue. It calls the OnPause virtual function in which you can
// specify the actions to take when the service pauses. If an error occurs,
// the error will be logged in the Application event log, and the service
// will become running.
//
void CServiceBase::Pause()
{
try
{
// 告诉SCM暂停中
SetServiceStatus(SERVICE_PAUSE_PENDING);
// 子类停止时要做的事情
OnPause();
// 告诉SCM已停止
SetServiceStatus(SERVICE_PAUSED);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Pause", dwError);
// Tell SCM that the service is still running.
SetServiceStatus(SERVICE_RUNNING);
}
catch (...)
{
// Log the error.
WriteEventLogEntry(L"Service failed to pause.", EVENTLOG_ERROR_TYPE);
// Tell SCM that the service is still running.
SetServiceStatus(SERVICE_RUNNING);
}
}
//
void CServiceBase::OnPause()
{
}
// 告诉SCM继续服务
void CServiceBase::Continue()
{
try
{
// Tell SCM that the service is resuming.
SetServiceStatus(SERVICE_CONTINUE_PENDING);
// Perform service-specific continue operations.
OnContinue();
// Tell SCM that the service is running.
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Continue", dwError);
// Tell SCM that the service is still paused.
SetServiceStatus(SERVICE_PAUSED);
}
catch (...)
{
// Log the error.
WriteEventLogEntry(L"Service failed to resume.", EVENTLOG_ERROR_TYPE);
// Tell SCM that the service is still paused.
SetServiceStatus(SERVICE_PAUSED);
}
}
//
void CServiceBase::OnContinue()
{
}
//
void CServiceBase::Shutdown()
{
try
{
// 子类关机前做的操作.
OnShutdown();
// 告诉SCM停止服务
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Shutdown", dwError);
}
catch (...)
{
// Log the error.
WriteEventLogEntry(L"Service failed to shut down.", EVENTLOG_ERROR_TYPE);
}
}
//
void CServiceBase::OnShutdown()
{
}
#pragma endregion
#pragma region Helper Functions
//
// FUNCTION: CServiceBase::SetServiceStatus(DWORD, DWORD, DWORD)
//
// PURPOSE: 设置服务状态并告诉给SCM
//
// PARAMETERS:
// * dwCurrentState - 服务当前状态码
// * dwWin32ExitCode - 服务的错误退出码
// * dwWaitHint - 挂起服务的大概时间(毫秒)
//
void CServiceBase::SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure of the service.
m_status.dwCurrentState = dwCurrentState;
m_status.dwWin32ExitCode = dwWin32ExitCode;
m_status.dwWaitHint = dwWaitHint;
m_status.dwCheckPoint =
((dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED)) ?
0 : dwCheckPoint++;
// Report the status of the service to the SCM.
::SetServiceStatus(m_statusHandle, &m_status);
}
//
// PURPOSE: 写日志到Windows应用程序日志中
//
// PARAMETERS:
// * pszMessage - 日志内容
// * wType - 日志类型,有以下几种:
//
// EVENTLOG_SUCCESS
// EVENTLOG_AUDIT_FAILURE
// EVENTLOG_AUDIT_SUCCESS
// EVENTLOG_ERROR_TYPE
// EVENTLOG_INFORMATION_TYPE
// EVENTLOG_WARNING_TYPE
//
void CServiceBase::WriteEventLogEntry(PWSTR pszMessage, WORD wType)
{
HANDLE hEventSource = NULL;
LPCWSTR lpszStrings[2] = { NULL, NULL };
hEventSource = RegisterEventSource(NULL, m_name);
if (hEventSource)
{
lpszStrings[0] = m_name;
lpszStrings[1] = pszMessage;
ReportEvent(hEventSource, // Event log handle
wType, // Event type
0, // Event category
0, // Event identifier
NULL, // No security identifier
2, // Size of lpszStrings array
0, // No binary data
lpszStrings, // Array of strings
NULL // No binary data
);
DeregisterEventSource(hEventSource);
}
}
// PURPOSE: 写错误日志到Windows 应用程序日志中
//
// PARAMETERS:
// * pszFunction - 错误信息
// * dwError - 错误码
//
void CServiceBase::WriteErrorLogEntry(PWSTR pszFunction, DWORD dwError)
{
wchar_t szMessage[260];
StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
L"%s failed w/err 0x%08lx", pszFunction, dwError);
WriteEventLogEntry(szMessage, EVENTLOG_ERROR_TYPE);
}
#pragma endregion
根据以上代码,可以总结出以下图:
可以看到启动的流程,需要做一些准备操作,才可以告诉SCM,我启动成功了。
停止和暂停的流程,只需要报告给SCM就可以了。
现在的代码并没有什么业务,只是启动、停止。
我们可以在OnStart和OnStop里面写业务代码
我们写一个类,继承CServiceBase
#include "ServiceBase.h"
#include <string>
using std::wstring;
class CImpService : public CServiceBase
{
public:
CImpService(PWSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
virtual ~CImpService(void);
protected:
virtual void OnStart(DWORD dwArgc, PWSTR *pszArgv);
virtual void OnStop();
private:
};
CImpService::CImpService(PWSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
: CServiceBase(pszServiceName, fCanStop, fCanShutdown, fCanPauseContinue)
{
}
CImpService::~CImpService(void)
{
}
void CImpService::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
{
// Log a service start message to the Application log.
WriteEventLogEntry(L"CImpService in OnStart",
EVENTLOG_INFORMATION_TYPE);
//业务代码
}
//
void CImpService::OnStop()
{
//业务代码
WriteEventLogEntry(L"CImpService in OnStop",
EVENTLOG_INFORMATION_TYPE);
}
服务的安装
void InstallService(PWSTR pszServiceName,
PWSTR pszDisplayName,
DWORD dwStartType,
PWSTR pszDependencies,
PWSTR pszAccount,
PWSTR pszPassword)
{
wchar_t szPath[MAX_PATH];
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
//获取当前exe的路径
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
{
wprintf(L"GetModuleFileName failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}
// 打开SCM
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT |
SC_MANAGER_CREATE_SERVICE);
if (schSCManager == NULL)
{
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}
// 安装服务
schService = CreateService(
schSCManager, // SCManager database
pszServiceName, // 服务名称
pszDisplayName, // 显示的名称
SERVICE_QUERY_STATUS, // 安全性访问权限
// 服务类型
// 如果是SERVICE_WIN32_OWN_PROCESS 或 SERVICE_WIN32_SHARE_PROCESS
// 且使用LocalSystem帐号来运行该服务程序,则还可以附加SERVICE_INTERACTIVE_PROCESS
//
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
dwStartType, // 服务启动选项
SERVICE_ERROR_NORMAL, // 发生错误的保护措施
szPath, // 服务的二进制文件路径
NULL, // 加载服务组的名称,NULL不加载
NULL, // 不更改组现有的标签
pszDependencies, // 依赖的服务,为NULL代表没有依赖
pszAccount, // 服务的运行账号
pszPassword // 服务运行账号的密码
);
if (schService == NULL)
{
wprintf(L"CreateService failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}
wprintf(L"%s is installed.\n", pszServiceName);
Cleanup:
// Centralized cleanup for all allocated resources.
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}
服务的卸载
void UninstallService(PWSTR pszServiceName)
{
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
SERVICE_STATUS ssSvcStatus = {};
// 打开SCM
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schSCManager == NULL)
{
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}
//打开服务,带停止和查询以及删除权限
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP |
SERVICE_QUERY_STATUS | DELETE);
if (schService == NULL)
{
wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}
// 尝试停止服务
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
{
wprintf(L"Stopping %s.", pszServiceName);
//查询服务状态
while (QueryServiceStatus(schService, &ssSvcStatus))
{
//如果服务状态 等于 正在停止
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
wprintf(L".");
}
else break;//不等于则调出
}
//如果服务状态 等于 停止
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED)
{
wprintf(L"\n%s is stopped.\n", pszServiceName);
}
else
{
//服务状态不等于停止
wprintf(L"\n%s failed to stop.\n", pszServiceName);
}
}
// 调用删除服务函数
if (!DeleteService(schService))
{
wprintf(L"DeleteService failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}
wprintf(L"%s is removed.\n", pszServiceName);
Cleanup:
// Centralized cleanup for all allocated resources.
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}
更新服务描述
/**
* 更新(添加)服务描述。
* @param serviceName : 服务名称。
* @param serviceDescription : 服务描述。
*/
bool updateServiceDescription(LPTSTR serviceName, LPTSTR serviceDescription)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
SERVICE_DESCRIPTION sd;
//LPTSTR szDesc = TEXT(serviceDescription);
// 打开SCM
schSCManager = ::OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
//printf("OpenSCManager failed (%d)\n", GetLastError());
return false;
}
// 获取服务句柄
schService = ::OpenService(
schSCManager, // SCM database
serviceName, // name of service
SERVICE_CHANGE_CONFIG); // need change config access
if (schService == NULL)
{
//printf("OpenService failed (%d)\n", GetLastError());
::CloseServiceHandle(schSCManager);
return false;
}
// 改变服务描述
sd.lpDescription = serviceDescription;
if (!::ChangeServiceConfig2(
schService, // handle to service
SERVICE_CONFIG_DESCRIPTION, // change: description
&sd)) // new description
{
//printf("ChangeServiceConfig2 failed\n");
::CloseServiceHandle(schService);
::CloseServiceHandle(schSCManager);
return false;
}
//else printf("Service description updated successfully.\n");
::CloseServiceHandle(schService);
::CloseServiceHandle(schSCManager);
return true;
}
结尾
Windows服务C++版就到这里,稍加改造便可以在项目中使用。