工具类库系列(八)-WinService

第八个工具类: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;
		}

	}
}



PS:
其中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对象用来输出日志,或者直接把日志相关的代码注释掉

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值