第八个工具类:WinService
这是一个windows下用的工具类,用于将exe(通常是服务程序)注册成windows系统服务
windows环境下运行的服务端程序,一般就会以系统服务的形式运行
1、在无人值守的情况下,可以设置,出错后自动重启
2、注销当前用户,进程不会被关闭,因为系统服务是运行在SYSTEM用户下的
3、可以设置开机自动启动,维护方便,不用开机之后,再去一个一个的执行exe
用到的都是一些windows service的API,不熟悉的同学可以先了解一下
这个工具类就是将这些个API封装起来,实现系统服务的添加/删除,运行/停止
用到了之前写好的Logger,用于服务功能的log输出
示例代码:
一个叫Test的小程序,没有什么功能
就是将自己注册成系统服务,
实现一下ServiceMain和ServiceCtrl(这两个函数必须申明为WINAPI,函数名无所谓,参数类型是固定的),就是服务程序入口函数和回调函数,对应启动服务和停止服务,内部就是直接调用WinService的Run和Stop
主逻辑就是一个循环,1秒输出一次log
main函数则处理下程序输入参数:
install:注册一个名字叫Test的系统服务,
uninstall:删除这个Test系统服务
run:运行这个服务(这个参数是这个工具类人为默认指定的运行参数,如果想更换,请修改WinService::Install中代码)
其他情况就以cmd方式运行
#include "Logger.h"
#include "WinService.h"
using namespace common::tool;
WinService g_WinService;
void WINAPI ServiceCtrl(DWORD opcode)
{
g_WinService.Stop(opcode);
}
void WINAPI ServiceMain(
DWORD servicesArgNum,
#ifdef UNICODE
LPWSTR *serviceArgVectors
#else // UNICODE
LPSTR *serviceArgVectors
#endif // UNICODE
)
{
g_WinService.Run(ServiceCtrl);
}
void run()
{
Logger logger("Test");
while (g_WinService.IsRunning())
{
LOG_INFO(logger) << "111";
Sleep(1000);
}
}
int main(int argc, char* argv[])
{
std::wstring serviceName = L"Test";
g_WinService.Init(serviceName, run);
// 安装服务
if (argc == 2 && strcmp(argv[1], "install") == 0)
{
g_WinService.Install();
}
// 卸载服务
else if (argc == 2 && strcmp(argv[1], "uninstall") == 0)
{
g_WinService.Uninstall();
}
// 运行服务,run参数为Install默认指定的运行参数
else if (argc == 2 && strcmp(argv[1], "run") == 0)
{
g_WinService.Start(ServiceMain);
}
// 不指定参数,直接运行exe
else
{
run();
}
return 0;
}
编译好之后,在cmd下(需要管理员权限)进入编译好的exe同级目录,执行:
Test.exe install,就会在服务中出现
Test.exe uninstall,就会删除这个服务
sc start Test,启动这个服务
sc stop Test,停止这个服务
最后完整代码
WinService.h
#ifndef __WinService_h__
#define __WinService_h__
#include <string>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
namespace common{
namespace tool{
class WinService
{
public:
WinService();
~WinService();
public:
void Init(
#ifdef UNICODE
const std::wstring& serviceName,
#else // UNICODE
const std::string& serviceName,
#endif // UNICODE
void(*runFun)());
private:
#ifdef UNICODE
std::wstring m_ServiceName;
#else // UNICODE
std::string m_ServiceName;
#endif // UNICODE
void(*m_RunFun)();
#ifdef WIN32
public:
bool Install();
bool IsInstalled();
bool Uninstall();
void Start(LPSERVICE_MAIN_FUNCTION mainFun);
void Run(LPHANDLER_FUNCTION ctrlFun);
void Stop(DWORD opcode);
private:
void UpdateServiceStatus(DWORD currentState, DWORD exitCode, DWORD waitHint);
SERVICE_STATUS_HANDLE m_StatusHandle;
SERVICE_STATUS m_Status;
HANDLE m_StopEvent;
#endif // WIN32
public:
bool IsRunning();
private:
bool m_Running;
};
}
}
#endif
WinService.cpp
#include "WinService.h"
#include "Logger.h"
#include "ToolDefine.h"
namespace common{
namespace tool{
WinService::WinService()
{
m_Running = true;
}
WinService::~WinService()
{
}
void WinService::Init(
#ifdef UNICODE
const std::wstring& serviceName,
#else // UNICODE
const std::string& serviceName,
#endif // UNICODE
void(*runFun)())
{
m_ServiceName = serviceName;
m_RunFun = runFun;
#ifdef WIN32
m_StatusHandle = NULL;
m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_Status.dwCurrentState = SERVICE_STOPPED;
m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
m_Status.dwWin32ExitCode = 0;
m_Status.dwServiceSpecificExitCode = 0;
m_Status.dwCheckPoint = 0;
m_Status.dwWaitHint = 0;
m_StopEvent = NULL;
#endif
}
#ifdef WIN32
bool WinService::Install()
{
if (IsInstalled())
{
LOG_ERROR(g_LibToolLog) << m_ServiceName << " is Installed";
return true;
}
//打开服务控制管理器
SC_HANDLE scm = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (scm != NULL)
{
#ifdef UNICODE
wchar_t filePath[MAX_PATH + 1];
::GetModuleFileName(NULL, filePath, MAX_PATH);
std::wstring binaryPathName;
binaryPathName = L"\"";
binaryPathName += filePath;
binaryPathName += L"\" run";
#else // #ifdef UNICODE
char filePath[MAX_PATH + 1];
::GetModuleFileName(NULL, filePath, MAX_PATH + 1);
std::string binaryPathName;
binaryPathName = "\"";
binaryPathName += filePath;
binaryPathName += "\" run";
#endif // #ifdef UNICODE
//创建服务
SC_HANDLE service = ::CreateService(
scm,
m_ServiceName.c_str(),
m_ServiceName.c_str(),
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
binaryPathName.c_str(),
NULL,
NULL,
NULL,
NULL,
NULL);
if (service != NULL)
{
::CloseServiceHandle(service);
service = ::OpenService(scm, m_ServiceName.c_str(), GENERIC_ALL);
SERVICE_FAILURE_ACTIONS failAction;
SC_ACTION action[3];
for (UINT i = 0; i < sizeof(action) / sizeof(SC_ACTION); i++)
{
action[i].Delay = 60 * 1000; //1分钟后重新启动服务
action[i].Type = SC_ACTION_NONE; //失败后不作任何操作
}
failAction.dwResetPeriod = 60 * 60 * 24; //1天后,重置失败计数
failAction.lpRebootMsg = NULL;
failAction.lpCommand = NULL;
failAction.cActions = 3;
failAction.lpsaActions = action;
ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, &failAction);
::CloseServiceHandle(service);
::CloseServiceHandle(scm);
LOG_INFO(g_LibToolLog) << "Create service " << m_ServiceName;
return true;
}
else
{
::CloseServiceHandle(scm);
LOG_ERROR(g_LibToolLog) << "Couldn't create service " << m_ServiceName << " lastError " << GetLastError();
return false;
}
}
else
{
DWORD res = GetLastError();
LOG_ERROR(g_LibToolLog) << "Couldn't open service manager " << m_ServiceName << " lastError " << GetLastError();
return false;
}
}
bool WinService::IsInstalled()
{
//打开服务控制管理器
SC_HANDLE scm = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (scm != NULL)
{
//打开服务
SC_HANDLE service = ::OpenService(scm, m_ServiceName.c_str(), SERVICE_QUERY_CONFIG);
if (service != NULL)
{
::CloseServiceHandle(service);
::CloseServiceHandle(scm);
return true;
}
else
{
::CloseServiceHandle(scm);
return false;
}
}
else
{
LOG_ERROR(g_LibToolLog) << "Couldn't open service manager " << m_ServiceName << " lastError " << GetLastError();
return false;
}
}
bool WinService::Uninstall()
{
if (!IsInstalled())
{
LOG_ERROR(g_LibToolLog) << m_ServiceName << " not Installed";
return true;
}
SC_HANDLE scm = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (scm != NULL)
{
SC_HANDLE service = ::OpenService(scm, m_ServiceName.c_str(), SERVICE_STOP | DELETE);
if (service != NULL)
{
SERVICE_STATUS status;
::ControlService(service, SERVICE_CONTROL_STOP, &status);
//删除服务
BOOL del = ::DeleteService(service);
::CloseServiceHandle(service);
::CloseServiceHandle(scm);
LOG_INFO(g_LibToolLog) << "Delete service " << m_ServiceName;
return (del == TRUE);
}
else
{
::CloseServiceHandle(scm);
LOG_ERROR(g_LibToolLog) << "Couldn't create service " << m_ServiceName << " lastError " << GetLastError();
return false;
}
}
else
{
LOG_ERROR(g_LibToolLog) << "Couldn't open service manager " << m_ServiceName << " lastError " << GetLastError();
return false;
}
}
void WinService::Start(LPSERVICE_MAIN_FUNCTION mainFun)
{
SERVICE_TABLE_ENTRY st[] =
{
{ const_cast<wchar_t *>(m_ServiceName.c_str()), mainFun },
{ NULL, NULL }
};
if (!::StartServiceCtrlDispatcher(st))
{
LOG_ERROR(g_LibToolLog) << "Register Service Main Function error " << m_ServiceName << " lastError " << GetLastError();
}
}
void WinService::Run(LPHANDLER_FUNCTION ctrlFun)
{
m_StatusHandle = RegisterServiceCtrlHandler(m_ServiceName.c_str(), ctrlFun);
if (m_StatusHandle != NULL)
{
m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_Status.dwServiceSpecificExitCode = 0;
UpdateServiceStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
m_StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_StopEvent != NULL)
{
UpdateServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
m_RunFun();
WaitForSingleObject(m_StopEvent, INFINITE);
UpdateServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
}
else
{
LOG_ERROR(g_LibToolLog) << "CreateEvent error " << m_ServiceName;
UpdateServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
}
}
else
{
LOG_ERROR(g_LibToolLog) << "Handler not installed " << m_ServiceName;
}
}
void WinService::Stop(DWORD opcode)
{
switch (opcode)
{
case SERVICE_CONTROL_STOP:
{
UpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(m_StopEvent);
UpdateServiceStatus(m_Status.dwCurrentState, NO_ERROR, 0);
m_Running = false;
}
break;
default:
break;
}
}
void WinService::UpdateServiceStatus(DWORD currentState, DWORD exitCode, DWORD waitHint)
{
static DWORD checkPoint = 1;
m_Status.dwCurrentState = currentState;
m_Status.dwWin32ExitCode = exitCode;
m_Status.dwWaitHint = waitHint;
if (currentState == SERVICE_START_PENDING)
{
m_Status.dwControlsAccepted = 0;
}
else
{
m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ((currentState == SERVICE_RUNNING) ||
(currentState == SERVICE_STOPPED))
{
m_Status.dwCheckPoint = 0;
}
else
{
m_Status.dwCheckPoint = checkPoint++;
}
SetServiceStatus(m_StatusHandle, &m_Status);
}
#endif
bool WinService::IsRunning()
{
return m_Running;
}
}
}
其中ToolDefine.h里面就是定义了一个全局的g_LibToolLog
ToolDefine.h
#ifndef __ToolDefine_h__
#define __ToolDefine_h__
//libtool专用log
namespace common{
namespace tool{
class Logger;
}
}
extern common::tool::Logger g_LibToolLog;
#endif
ToolDefine.cpp
#include "ToolDefine.h"
#include "Logger.h"
using namespace common::tool;
Logger g_LibToolLog("LibTool");
之所以把这个Logger对象定义成一个全局变量,写在另一个头文件里面
是因为我自己这一系列的工具类是打包成一个lib的,这个lib中有很多工具类,所有这些类如果有输出日志的需求,用的都是这个g_LibToolLog
所以读者如果没有类似这样的需求的话,可以自行修改代码,直接在WinService.cpp里面定义一个logger对象用来输出日志,或者直接把日志相关的代码注释掉