windows服务开发

参考博客:
C++调用StartService启动服务失败1053分析与解决
StartServiceCtrlDispatcher函数
指定的服务已标记为删除
安装和卸载服务

windows服务开发分为两个步骤:

  1. 编写服务程序.exe
  2. 安装服务程序,使在services.msc里面可以看见自己的服务

编写服务程序

问:为什么需要编写服务程序?是不是随便一个exe都可以作为服务程序运行?
答:服务程序必须主动调用StartServiceCtrlDispatcher上报自己的当前状态,否则服务控制台由于不知道服务的运行状态的而报错,例如:StartService启动服务失败1053。所以开发服务程序的时候我们必须通过StartServiceCtrlDispatcher上报状态并且处理服务事件。

  1. 调用StartServiceCtrlDispatcher,设置服务事件回调。如果程序作为非服务状态运行则会返回错误:ERROR_FAILED_SERVICE_CONTROLLER_CONNECT,程序会一直阻塞运行,直至SERVICE_STOPPED状态才返回
  2. 注册并相应服务控制事件,例如服务控制台停止事件
  3. 上报服务当前状态
  4. 设置服务异常策略(可选)
    在这里插入图片描述

Msc.h

#pragma once
#include <Windows.h>
#include <string>
/*
*服务控制要以服务运行才可以,如果直接调试会报错ERROR_FAILED_SERVICE_CONTROLLER_CONNECT : 服务进程无法连接到服务控制器上
* 安装服务可调试
作为windows服务运行,首先得安装服务,安装完成之后启动服务。
服务分为两部分:
1.服务安装,卸载,启动,关闭
2.服务控制程序,停止,启动等
*/

//服务安装程序
class CMscInstaller
{
public:
	CMscInstaller(std::string msc_name,std::string exe_full_path,std::string exe_param = "",std::string msc_desc = "");
	~CMscInstaller();

	BOOL install(const std::vector<std::string>& start_service_cmd = std::vector<std::string>());//安装并启动服务
	BOOL uninstall();//停止并卸载服务
	BOOL set_failuer_action();
private:
	//使用后需要手动delete pConfig
	BOOL query_service_config(SC_HANDLE hService,QUERY_SERVICE_CONFIG*& pConfig);//查询服务配置
private:
	std::string m_msc_name;
	std::string m_msc_display_name;
	std::string m_msc_desc;
	std::string m_exe_cmd;
};

//如果作为服务控制程序,需要向服务报告自己的状态,否则服务控制台是不知道服务状态,会失败
class CMscController
{
public:
	//初始化服务控制句柄
	static void Init(std::string msc_name);
	//上报启动成功事件
	static void ReportStart();
	//上报停止成功事件
	static void ReportStop();
private:
	//如果作为服务程序需要报告自己的状态
	static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
	//服务主处理函数,内部添加自己的业务逻辑代码。可包装到doWork函数内部
	static VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv);
	//响应服务控制台事件,例如停止
	static VOID WINAPI SvcCtrlHandler(DWORD dwCtrl);

	static SERVICE_STATUS_HANDLE s_gSvcStatusHandle;
	static SERVICE_STATUS s_gSvcStatus;
	static std::string m_msc_name;
};

Msc.cpp

#include "stdafx.h"
#include "Msc.h"
#include <windows.h>
#include <tchar.h>
class CSafeSCHandle
{
public:
	CSafeSCHandle(const SC_HANDLE& hscm) :m_hscm(hscm){};
	~CSafeSCHandle()
	{
		if (m_hscm)
		{
			CloseServiceHandle(m_hscm);
		}
	}

	operator const SC_HANDLE&(){ return m_hscm; }
private:
	const SC_HANDLE &m_hscm;
};

CMscInstaller::CMscInstaller(std::string msc_name, std::string exe_full_path, std::string exe_param, std::string msc_desc)
{
	m_msc_name = msc_name;
	m_msc_display_name = msc_desc;
	m_exe_cmd = exe_full_path + _T(" ") + exe_param;
	m_msc_desc = msc_desc;
	XLOGI("CMscInstaller name:%s,exe_full_path:%s,exe_param:%s", m_msc_name.c_str(), exe_full_path.c_str(), exe_param.c_str());
}


CMscInstaller::~CMscInstaller()
{
}

BOOL CMscInstaller::install(const std::vector<std::string>& start_service_cmd)
{
	//打开msc服务管理器
	CSafeSCHandle hScm(OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
	if (hScm == NULL)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller name:%s,start,OpenSCManager failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}
	std::vector<LPCTSTR> vecCmd(start_service_cmd.size(),nullptr);
	for (int i = 0; i < start_service_cmd.size(); i++)
	{
		vecCmd[i] = start_service_cmd[i].c_str();
	}

	//如果服务已经存在则直接start
	CSafeSCHandle hService(OpenService(hScm, m_msc_name.c_str(), SERVICE_ALL_ACCESS));
	if (hService != NULL)
	{
		if (!m_msc_desc.empty())
		{
			SERVICE_DESCRIPTION sd;
			sd.lpDescription = (char*)m_msc_desc.c_str();
			ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sd);
		}

		SERVICE_STATUS status;
		QueryServiceStatus(hService, &status);
		if (status.dwCurrentState == SERVICE_RUNNING)
		{
			return TRUE;
		}

		if (StartService(hService, start_service_cmd.size(), start_service_cmd.size() == 0 ? NULL: &vecCmd[0]))
		{
			XLOGI("CMscInstaller name:%s,start,StartService OK", m_msc_name.c_str());
			return TRUE;
		}
		else
		{
			DWORD dwErrcode = GetLastError();
			XLOGI("CMscInstaller name:%s,start,StartService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
			return FALSE;
		}
	}

	//如果服务不存在则创建
	CSafeSCHandle hCService(CreateService(hScm, m_msc_name.c_str(), m_msc_display_name.c_str(), SERVICE_ALL_ACCESS,
		SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, m_exe_cmd.c_str(),
		NULL, NULL, "", NULL, ""));

	if (hCService == NULL)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller name:%s,start,CreateService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}

	if (!m_msc_desc.empty())
	{
		SERVICE_DESCRIPTION sd;
		sd.lpDescription = (char *)m_msc_desc.c_str();
		ChangeServiceConfig2(hCService, SERVICE_CONFIG_DESCRIPTION, &sd);
	}
	SERVICE_STATUS status;
	QueryServiceStatus(hCService, &status);
	if (status.dwCurrentState == SERVICE_RUNNING)
	{
		XLOGI("CMscInstaller name:%s,start,StartService OK,is already runing", m_msc_name.c_str());
		return TRUE;
	}
	
	if (StartService(hCService, start_service_cmd.size(), start_service_cmd.size() == 0 ? NULL : &vecCmd[0]))
	{
		XLOGI("CMscInstaller name:%s,start,StartService OK", m_msc_name.c_str());
		return TRUE;
	}
	else
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller name:%s,start,StartService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}
}

BOOL CMscInstaller::query_service_config(SC_HANDLE hService,QUERY_SERVICE_CONFIG* &pConfig)
{
	if (pConfig != nullptr)
		delete pConfig;
	pConfig = nullptr;

	DWORD dwSize = 0;
	pConfig = (QUERY_SERVICE_CONFIG*)(new char[sizeof(QUERY_SERVICE_CONFIG)]);
	
	if (QueryServiceConfig(hService, pConfig, sizeof(QUERY_SERVICE_CONFIG), &dwSize)) return TRUE;

	delete pConfig;
	pConfig = nullptr;

	DWORD dwErrCode = GetLastError();
	if (ERROR_INSUFFICIENT_BUFFER == dwErrCode)
	{
		pConfig = (QUERY_SERVICE_CONFIG*)(new char[dwSize]);
		if (QueryServiceConfig(hService, pConfig, dwSize, &dwSize))
		{
			return TRUE;
		}
	}
	return FALSE;
}

BOOL CMscInstaller::uninstall()
{
	//打开msc服务管理器
	CSafeSCHandle hScm(OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
	if (hScm == NULL)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller name:%s,stop,OpenSCManager failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}
	//如果服务已经存在则直接start
	CSafeSCHandle hService(OpenService(hScm, m_msc_name.c_str(), SERVICE_ALL_ACCESS));
	if (hService == NULL)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller name:%s,stop,OpenService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}

	//查询服务状态
	SERVICE_STATUS status;
	QueryServiceStatus(hService, &status);
	if (status.dwCurrentState == SERVICE_RUNNING)
	{
		//停止服务
		ControlService(hService, SERVICE_CONTROL_STOP, &status);
		if (status.dwCurrentState != NO_ERROR)
		{
			DWORD dwErrcode = GetLastError();
			XLOGI("CMscInstaller name:%s,stop,ControlService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
			return FALSE;
		}
	}

	//如果卸载服务
	if (status.dwCurrentState == SERVICE_STOPPED)
	{
		if (DeleteService(hService))
		{
			XLOGI("CMscInstaller name:%s,stop,DeleteService OK", m_msc_name.c_str());
		}
		else
		{
			DWORD dwErrcode = GetLastError();
			XLOGI("CMscInstaller name:%s,stop,DeleteService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		}
	}

	XLOGI("CMscInstaller name:%s,stop,ControlService OK", m_msc_name.c_str());
	return TRUE;
}

BOOL CMscInstaller::set_failuer_action()
{
	//打开msc服务管理器
	CSafeSCHandle hScm(OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
	if (hScm == NULL)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller set_failuer_action name:%s,start, OpenSCManager failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}

	//如果服务已经存在则直接start
	CSafeSCHandle hService(OpenService(hScm, m_msc_name.c_str(), SERVICE_ALL_ACCESS));
	if (hService == NULL)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller set_failuer_action name:%s,start,OpenService failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}
	SERVICE_FAILURE_ACTIONS sdBuf = { 0 };
	sdBuf.lpRebootMsg = NULL;
	sdBuf.lpCommand = NULL;
	sdBuf.dwResetPeriod = 3600 * 24;

	SC_ACTION action[3];
	action[0].Delay = 60 * 1000;
	action[0].Type = SC_ACTION_RESTART;//重启服务

	action[1].Delay = 2 * 60 * 1000;
	action[1].Type = SC_ACTION_RESTART;

	action[2].Delay = 2 * 60 * 1000;
	action[2].Type = SC_ACTION_NONE;

	sdBuf.cActions = 3;
	sdBuf.lpsaActions = action;

	if (!ChangeServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, &sdBuf))
	{
		DWORD dwErrcode = GetLastError();
		XLOGI("CMscInstaller set_failuer_action name:%s,start,ChangeServiceConfig2 failed,errcode:%d", m_msc_name.c_str(), dwErrcode);
		return FALSE;
	}
	XLOGI("CMscInstaller set_failuer_action OK");
	return TRUE;
}



SERVICE_STATUS_HANDLE CMscController::s_gSvcStatusHandle;
SERVICE_STATUS CMscController::s_gSvcStatus;
std::string CMscController::m_msc_name = "";

void CMscController::Init(std::string msc_name)
{
	m_msc_name = msc_name;
	char szName[MAX_PATH] = "";
	_stprintf_s<MAX_PATH>(szName, "%s", m_msc_name.c_str());
	SERVICE_TABLE_ENTRY DispatchTable[] ={
		{ szName, (LPSERVICE_MAIN_FUNCTION)SvcMain },
		{ NULL, NULL }};

	if (!StartServiceCtrlDispatcher(DispatchTable))
	{
		DWORD dwErrcode = GetLastError();
		XLOGI(_T("CMscController StartServiceCtrlDispatcher failed,errcode:%d"), dwErrcode);
	}
	XLOGI("CMscController StartServiceCtrlDispatcher Ok");
}

void CMscController::ReportStart()
{
	XLOGI("CMscController ReportStart");
	ReportSvcStatus(SERVICE_RUNNING, 0, 0);
}

void CMscController::ReportStop()
{
	XLOGI("CMscController ReportStop");
	ReportSvcStatus(SERVICE_STOPPED, 0, 0);
}

extern int doWork();
//服务启动入口
VOID WINAPI CMscController::SvcMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
	XLOGI("SvcMain arg count:%d", dwArgc);
	// Register the handler function for the service 
	for (int i = 0;i < dwArgc;++i)
	{
		XLOGI("SvcMain arg[%d]:%s", i, lpszArgv[i]);
	}
	s_gSvcStatusHandle = RegisterServiceCtrlHandler(m_msc_name.c_str(), SvcCtrlHandler);
	if (!s_gSvcStatusHandle)
	{
		DWORD dwErrcode = GetLastError();
		XLOGI(_T("CMscController RegisterServiceCtrlHandler failed,errcode:%d"), dwErrcode);
		return;
	}
	XLOGI("RegisterServiceCtrlHandler OK");
	// These SERVICE_STATUS members remain as set here 
	s_gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	s_gSvcStatus.dwServiceSpecificExitCode = 0;
	// Report initial status to the SCM
	ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);

	CMscController::ReportStart();
	doWork();
	CMscController::ReportStop();
}


void CMscController::ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
	static DWORD dwCheckPoint = 1;

	// Fill in the SERVICE_STATUS structure.
	s_gSvcStatus.dwCurrentState = dwCurrentState;
	s_gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
	s_gSvcStatus.dwWaitHint = dwWaitHint;

	if (dwCurrentState == SERVICE_START_PENDING)
		s_gSvcStatus.dwControlsAccepted = 0;
	else s_gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

	if ((dwCurrentState == SERVICE_RUNNING) ||
		(dwCurrentState == SERVICE_STOPPED))
		s_gSvcStatus.dwCheckPoint = 0;
	else s_gSvcStatus.dwCheckPoint = dwCheckPoint++;

	SetServiceStatus(s_gSvcStatusHandle, &s_gSvcStatus);
}
extern atomic_bool g_bquit;
//每当使用controlService函数向服务发送控制代码时,由SCM调用
VOID WINAPI CMscController::SvcCtrlHandler(DWORD dwCtrl)
{
	// Handle the requested control code. 
	switch (dwCtrl)
	{
	case SERVICE_CONTROL_STOP:
	{
		XLOGI("SvcCtrlHandler SERVICE_CONTROL_STOP");
		ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
		//通知DoWork退出
		g_bquit = true;
		return;
	}
	case SERVICE_CONTROL_INTERROGATE:
		break;

	default:
		break;
	}
}

服务安装,卸载,运行

int doWork()
{
	while (!g_bquit)
	{
		Sleep(100);
	}
	XLOGI("do work exit");
	return 0;
}

std::vector<std::string> format_cmd(const std::map<std::string, std::string>& mapKV)
{
	std::vector<std::string> vret;
	for (auto& item : mapKV)
	{
		vret.push_back(fmt::format("--{0}={1}", item.first, item.second));
	}
	return vret;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	std::string strWork = GetApplicationDir();
	SetCurrentDirectory(strWork.c_str());
	_mkdir("./log");
	_mkdir("./config");
	jeflib::CrashDumper::Init();

	std::string file_vsersion;
	jeflib::Win32Util::GetModuleVersion(hInstance, file_vsersion);
	XLOGI("app version:%s compile datetime:%s %s", file_vsersion.c_str(), __DATE__, __TIME__);
	XLOGI("app start,cmdLine:%s", lpCmdLine ? lpCmdLine : "");

	std::string strCmd = lpCmdLine ? std::string(lpCmdLine) : std::string("");
	auto kv = jeflib::StringUtil::splitKV(strCmd, " --", "=");
	std::map<std::string, std::string> mapKV;
	for (auto& it : kv)
	{
		mapKV[hv::trim(it.first)] = hv::trim(it.second);
	}

	const char* SRVICE_NAME = _T("zhanba_server");
	//指定作为端运行--client
	if (mapKV.count("client"))
	{
		HANDLE hMutex = ::CreateMutex(NULL, FALSE, _T("Global\\98E404B273F14D458143A621E1047909"));
		if (GetLastError() == ERROR_ALREADY_EXISTS)
		{
			ReleaseMutex(hMutex);
			XLOGI("App is Runing,this App[%I64d] will exit", GetCurrentProcessId());
			return 0;
		}
		doWork();
		return 0;
	}
	//作为服务运行--services
	else if (mapKV.count("services"))
	{
		//默认作为服务运行
		XLOGI("run mode serveices");
		CMscController::Init(SRVICE_NAME);
		XLOGI("CMscController Init quit");
	}
	//停止并卸载运行--uninstall
	else if(mapKV.count("uninstall"))
	{
		XLOGI("uninstall serveices");
		TCHAR szExePath[MAX_PATH] = _T("");
		GetModuleFileName(NULL, szExePath, MAX_PATH);
		CMscInstaller msc(SRVICE_NAME, szExePath, "--services", "xx更新服务");
		msc.uninstall();
		XLOGI("CMscController uninstall quit");
	}
	//安装并运行服务
	else
	{
		//如果安装正在进行,则后续安装直接退出
		HANDLE hMutex = ::CreateMutex(NULL, FALSE, _T("Global\\install_98E404B273F14D458143A621E1047909"));
		if (GetLastError() == ERROR_ALREADY_EXISTS)
		{
			ReleaseMutex(hMutex);
			XLOGI("App is install,this App[%I64d] will exit", GetCurrentProcessId());
			return 0;
		}

		XLOGI("install serveices");
		TCHAR szExePath[MAX_PATH] = _T("");
		GetModuleFileName(NULL, szExePath, MAX_PATH);
		CMscInstaller msc(SRVICE_NAME, szExePath, "--services", "xx更新服务");
		//如果安装失败则卸载重装
		if (!msc.install(format_cmd(mapKV)))
		{
			XLOGI("install failed,will uninstall and try install");
			BOOL bret = msc.uninstall();
			XLOGI("uninstall %s",bret ? "ok":"failed");
			bret = msc.install(format_cmd(mapKV));
			XLOGI("install %s", bret ? "ok" : "failed");
		}
		XLOGI("install quit");
	}
	//直接安全退出,不检查线程池(线程池可能此时在做其他耗时事情),可能产生异常dump
	quick_exit(0);
	return 0;
}

此时我们就可以安装服务程序,并且在services.msc中查看了。

Note:如果服务管理器打开了,则删除服务管理器会失败[指定的服务已标记为删除]。必须关闭重试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值