参考博客:
C++调用StartService启动服务失败1053分析与解决
StartServiceCtrlDispatcher函数
指定的服务已标记为删除
安装和卸载服务
windows服务开发分为两个步骤:
- 编写服务程序
.exe
- 安装服务程序,使在
services.msc
里面可以看见自己的服务
编写服务程序
问:为什么需要编写服务程序?是不是随便一个exe都可以作为服务程序运行?
答:服务程序必须主动调用StartServiceCtrlDispatcher
上报自己的当前状态,否则服务控制台由于不知道服务的运行状态的而报错,例如:StartService启动服务失败1053。所以开发服务程序的时候我们必须通过StartServiceCtrlDispatcher
上报状态并且处理服务事件。
- 调用
StartServiceCtrlDispatcher
,设置服务事件回调。如果程序作为非服务状态运行则会返回错误:ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
,程序会一直阻塞运行,直至SERVICE_STOPPED
状态才返回 - 注册并相应服务控制事件,例如服务控制台停止事件
- 上报服务当前状态
- 设置服务异常策略(可选)
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:如果服务管理器打开了,则删除服务管理器会失败[指定的服务已标记为删除]。必须关闭重试。