C/C++ 创建windows系统服务程序

Windows上面的程序可以分为前台运行和后台运行,像windows的系统服务都是属于后台程序。

那如何将一个程序变成后台运行呢?

可以在 cmd里面用sc命令来操作:Windows 服务的安装和卸载

此次重点介绍如果用c++代码创建一个Windows服务程序。

这里用到了2个创建服务的类和一个依赖头文件和一个使用类,具体解释请看注释,代码如下:

copy_disabler.h(这个为底层依赖头文件,可以不用关心具体类容)

#ifndef COPY_DISABLER_H_
#define COPY_DISABLER_H_

#define DISABLE_COPY(Type)\
	Type(const Type&) = delete;\
	Type& operator=(const Type&) = delete

#define DISABLE_MOVE(Type)\
	Type(Type&&) = delete;\
	Type& operator=(Type&&) = delete

#define DISABLE_COPY_MOVE(Type)\
	DISABLE_COPY(Type);\
	DISABLE_MOVE(Type)


#define DISABLE_COPY_AND_MOVE(Type)\
private:\
	Type(const Type&);\
	Type& operator=(const Type&);\
	Type(Type&&);\
	Type& operator=(Type&&);

#endif // COPY_DISABLER_H_

service_base.h(这个为底层依赖头文件,可以不用关心具体类容)

#ifndef SERVICE_BASE_H_
#define SERVICE_BASE_H_

#include <windows.h>
#include <atlstr.h>

#include "copy_disabler.h"

// Base Service class used to create windows services.
class ServiceBase 
{
public:
	//DISABLE_COPY_MOVE(ServiceBase);

	virtual ~ServiceBase() {}

	// Called by windows when starting the service.
	bool Run() 
	{
		return RunInternal(this);
	}

	const CString& GetName() const { return m_name; }
	const CString& GetDisplayName() const { return m_displayName; }
	const DWORD GetStartType() const { return m_dwStartType; }
	const DWORD GetErrorControlType() const { return m_dwErrorCtrlType; }
	const CString& GetDependencies() const { return m_depends; }

	// Account info service runs under.
	const CString& GetAccount() const { return m_account; }
	const CString& GetPassword() const { return m_password; }
protected:
	ServiceBase(const CString& name,
		const CString& displayName,
		DWORD dwStartType,
		DWORD dwErrCtrlType = SERVICE_ERROR_NORMAL,
		DWORD dwAcceptedCmds = SERVICE_ACCEPT_STOP,
		const CString& depends = _T(""),
		const CString& account = _T(""),
		const CString& password = _T(""));

	void SetStatus(DWORD dwState, DWORD dwErrCode = NO_ERROR, DWORD dwWait = 0);

	// TODO(Olster): Move out of class/make static.
	// Writes |msg| to Windows event log.
	void WriteToEventLog(const CString& msg, WORD type = EVENTLOG_INFORMATION_TYPE);

	virtual void OnStart(DWORD argc, TCHAR* argv[]) = 0;
	virtual void OnStop() {}
	virtual void OnPause() {}
	virtual void OnContinue() {}
	virtual void OnShutdown() {}

	virtual void OnSessionChange(DWORD /*evtType*/,
		WTSSESSION_NOTIFICATION* /*notification*/) {}
private:
	// Registers handle and starts the service.
	static void WINAPI SvcMain(DWORD argc, TCHAR* argv[]);

	// Called whenever service control manager updates service status.
	static DWORD WINAPI ServiceCtrlHandler(DWORD ctrlCode, DWORD evtType,
		void* evtData, void* context);

	static bool RunInternal(ServiceBase* svc);

	void Start(DWORD argc, TCHAR* argv[]);
	void Stop();
	void Pause();
	void Continue();
	void Shutdown();

	CString m_name;
	CString m_displayName;
	DWORD m_dwStartType;
	DWORD m_dwErrorCtrlType;
	CString m_depends;
	CString m_account;
	CString m_password;

	// Info about service dependencies and user account.
	bool m_hasDepends;/* = false*/;
	bool m_hasAcc;/* = false*/;
	bool m_hasPass;/* = false*/;

	SERVICE_STATUS m_svcStatus;
	SERVICE_STATUS_HANDLE m_svcStatusHandle;

	static ServiceBase* m_service;
	DISABLE_COPY_AND_MOVE(ServiceBase)
};

#endif // SERVICE_BASE_H_


Service_base.cpp(这个为底层依赖源文件,可以不用关心具体类容)

#include <stdafx.h>
#include "service_base.h"
#include <cassert>

ServiceBase* ServiceBase::m_service = nullptr;

ServiceBase::ServiceBase(const CString& name,
	const CString& displayName,
	DWORD dwStartType,
	DWORD dwErrCtrlType,
	DWORD dwAcceptedCmds,
	const CString& depends,
	const CString& account,
	const CString& password)
	: m_name(name),
	m_displayName(displayName),
	m_dwStartType(dwStartType),
	m_dwErrorCtrlType(dwErrCtrlType),
	m_depends(depends),
	m_account(account),
	m_password(password),
	m_svcStatusHandle(nullptr) 
{  

		m_hasDepends = !m_depends.IsEmpty();
		m_hasAcc = !m_account.IsEmpty();
		m_hasPass = !m_password.IsEmpty();
		m_svcStatus.dwControlsAccepted = dwAcceptedCmds;
		m_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
		m_svcStatus.dwCurrentState = SERVICE_START_PENDING;
		m_svcStatus.dwWin32ExitCode = NO_ERROR;
		m_svcStatus.dwServiceSpecificExitCode = 0;
		m_svcStatus.dwCheckPoint = 0;
		m_svcStatus.dwWaitHint = 0;
}

void ServiceBase::SetStatus(DWORD dwState, DWORD dwErrCode, DWORD dwWait) 
{
	m_svcStatus.dwCurrentState = dwState;
	m_svcStatus.dwWin32ExitCode = dwErrCode;
	m_svcStatus.dwWaitHint = dwWait;
	::SetServiceStatus(m_svcStatusHandle, &m_svcStatus);
}

void ServiceBase::WriteToEventLog(const CString& msg, WORD type) 
{
	HANDLE hSource = RegisterEventSource(nullptr, m_name);
	if (hSource) 
	{
		const TCHAR* msgData[2] = {m_name, msg};
		ReportEvent(hSource, type, 0, 0, nullptr, 2, 0, msgData, nullptr);
		DeregisterEventSource(hSource);
	}
}

// static
void WINAPI ServiceBase::SvcMain(DWORD argc, TCHAR* argv[]) 
{
	assert(m_service);
	m_service->m_svcStatusHandle = ::RegisterServiceCtrlHandlerEx(m_service->GetName(),ServiceCtrlHandler, NULL);
	if (!m_service->m_svcStatusHandle) 
	{
		m_service->WriteToEventLog(_T("Can't set service control handler"),EVENTLOG_ERROR_TYPE);
		return;
	}

	m_service->Start(argc, argv);
}

// static
DWORD WINAPI ServiceBase::ServiceCtrlHandler(DWORD ctrlCode, DWORD evtType, void* evtData, void* /*context*/) 
{
	switch (ctrlCode) 
	{
	case SERVICE_CONTROL_STOP:
		m_service->Stop();
		break;

	case SERVICE_CONTROL_PAUSE:
		m_service->Pause();
		break;

	case SERVICE_CONTROL_CONTINUE:
		m_service->Continue();
		break;

	case SERVICE_CONTROL_SHUTDOWN:
		m_service->Shutdown();
		break;

	case SERVICE_CONTROL_SESSIONCHANGE:
		m_service->OnSessionChange(evtType, reinterpret_cast<WTSSESSION_NOTIFICATION*>(evtData));
		break;

	default:
		break;
	}

	return 0;
}

bool ServiceBase::RunInternal(ServiceBase* svc) 
{
	m_service = svc;
	TCHAR* svcName = const_cast<CString&>(m_service->GetName()).GetBuffer();

	SERVICE_TABLE_ENTRY tableEntry[] = 
	{
		{svcName, SvcMain},
		{nullptr, nullptr}
	};

	return ::StartServiceCtrlDispatcher(tableEntry) == TRUE;
}

void ServiceBase::Start(DWORD argc, TCHAR* argv[]) 
{
	SetStatus(SERVICE_START_PENDING);
	OnStart(argc, argv);
	SetStatus(SERVICE_RUNNING);
}

void ServiceBase::Stop() 
{
	SetStatus(SERVICE_STOP_PENDING);
	OnStop();
	SetStatus(SERVICE_STOPPED);
}

void ServiceBase::Pause() 
{
	SetStatus(SERVICE_PAUSE_PENDING);
	OnPause();
	SetStatus(SERVICE_PAUSED);
}

void ServiceBase::Continue() 
{
	SetStatus(SERVICE_CONTINUE_PENDING);
	OnContinue();
	SetStatus(SERVICE_RUNNING);
}

void ServiceBase::Shutdown() 
{
	OnShutdown();
	SetStatus(SERVICE_STOPPED);
}

Service_installer.h(这个为底层依赖头文件,可以不用关心具体类容)

#ifndef SERVICE_INSTALLER_H_
#define SERVICE_INSTALLER_H_

#include "service_base.h"

class ServiceInstaller {
public:
	static bool Install(const ServiceBase& service);
	static bool Uninstall(const ServiceBase& service);
private:
	ServiceInstaller() {}
};

#endif // SERVICE_INSTALLER_H_


Service_installer.cpp(这个为底层依赖源文件,可以不用关心具体类容)

#include <stdafx.h>
#include "service_installer.h"

namespace 
{
	class ServiceHandle 
	{
		public:
			ServiceHandle(SC_HANDLE handle) : m_handle(handle) {}

			~ServiceHandle() 
			{
				if (m_handle) 
				{
					::CloseServiceHandle(m_handle);
				}
			}

			operator SC_HANDLE() 
			{
				return m_handle;
			}

		private:
			SC_HANDLE m_handle;
	};
}

//static
bool ServiceInstaller::Install(const ServiceBase& service) 
{
	CString escapedPath;
	TCHAR* modulePath = escapedPath.GetBufferSetLength(MAX_PATH);

	if (::GetModuleFileName(nullptr, modulePath, MAX_PATH) == 0) 
	{
		_tprintf(_T("Couldn't get module file name: %d\n"), ::GetLastError());
		return false;
	}

	escapedPath.ReleaseBuffer();
	escapedPath.Remove(_T('\"'));

	escapedPath = _T('\"') + escapedPath + _T('\"');

	ServiceHandle svcControlManager = ::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
	if (!svcControlManager) 
	{
		_tprintf(_T("Couldn't open service control manager: %d\n"), GetLastError());
		return false;
	}

	const CString& depends = service.GetDependencies();
	const CString& acc = service.GetAccount();
	const CString& pass = service.GetPassword();

	ServiceHandle servHandle = ::CreateService(svcControlManager,
		service.GetName(),
		service.GetDisplayName(),
		SERVICE_QUERY_STATUS,
		SERVICE_WIN32_OWN_PROCESS,
		service.GetStartType(),
		service.GetErrorControlType(),
		escapedPath,
		nullptr,
		nullptr,
		(depends.IsEmpty() ? nullptr : depends.GetString()),
		(acc.IsEmpty() ? nullptr : acc.GetString()),
		(pass.IsEmpty() ? nullptr : pass.GetString()));
	
	if (!servHandle) 
	{
		_tprintf(_T("Couldn't create service: %d\n"), ::GetLastError());
		return false;
	}

	return true;
}

//static
bool ServiceInstaller::Uninstall(const ServiceBase& service)
{
	ServiceHandle svcControlManager = ::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT);

	if (!svcControlManager) 
	{
		_tprintf(_T("Couldn't open service control manager: %d\n"), GetLastError());
		return false;
	}

	ServiceHandle servHandle = ::OpenService(svcControlManager, service.GetName(),
		SERVICE_QUERY_STATUS | SERVICE_STOP | DELETE);

	if (!servHandle) 
	{
		_tprintf(_T("Couldn't open service control manager: %d\n"), ::GetLastError());
		return false;
	}

	SERVICE_STATUS servStatus = {};
	if (::ControlService(servHandle, SERVICE_CONTROL_STOP, &servStatus)) 
	{
		_tprintf(_T("Stoping service %s\n"), service.GetName());

		while (::QueryServiceStatus(servHandle, &servStatus)) 
		{
			if (servStatus.dwCurrentState != SERVICE_STOP_PENDING) 
			{
				break;
			}
		}

		if (servStatus.dwCurrentState != SERVICE_STOPPED) 
		{
			_tprintf(_T("Failed to stop the service\n"));
		} 
		else 
		{
			_tprintf(_T("Service stopped\n"));
		}
	} 
	else 
	{
		_tprintf(_T("Didn't control service: %d\n"), ::GetLastError());
	}

	if (!::DeleteService(servHandle)) 
	{
		_tprintf(_T("Failed to delete the service: %d\n"), GetLastError());
		return false;
	}

	return true;
}


TestSysService.cpp(具体的使用类,详细的解释见注释)

// TestSysService.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "sysservice/service_base.h"
#include "sysservice/service_installer.h"

//此类继承基础服务类
class CTestService:public ServiceBase
{
public:
#ifdef _DEBUG
	CTestService(const CString&name)
		:ServiceBase(name,
		name,
		SERVICE_DEMAND_START,
		SERVICE_ERROR_NORMAL,
		SERVICE_ACCEPT_STOP
		){}
#else
	CTestService(const CString&name)
		:ServiceBase(name,
		name,
		SERVICE_AUTO_START,
		SERVICE_ERROR_NORMAL,
		SERVICE_ACCEPT_STOP
		){}
#endif
private:
	//服务开启时调用的接口(此接口不能阻塞,如果阻塞服务会一直显示开启中)
	void OnStart(DWORD argc, TCHAR* argv[])
	{
		OutputDebugString(_T("TestService:Application start running!!!\n"));
	}

	//服务关闭时调用的接口(此接口不能阻塞,如果阻塞服务会一直显示关闭中)
	void OnStop()
	{
		OutputDebugString(_T("TestService:Application end\n"));
	}
public:
	//如果是以服务的方式运行就到此函数里面直接调用ServiceBase的Run函数。此函数调用完以后,服务会调用OnStart函数。
	//如果是控制台的方式运行就到此函数里面写主要的逻辑代码。此函数一般也会到内部调用OnStart和OnStop函数。此函数为主线程,所以不能退出。
	bool Run(LPCTSTR param = _T(""))
	{
		if (_tcscmp(param, _T("console")) == 0)
		{
			//Todo:控制台运行处理 调用OnStart和OnStop
			
			TCHAR cinCmd[128];
			bool bStart = false;

			while(1)//控制台运行主线程不能退出 
			{
			_tprintf(_T("->input cmd\r\n"));

			_tscanf_s(_T("%s"), cinCmd, 128);
			if (_tcsncmp(cinCmd, _T("?"), 1) == 0) 
			{
				_tprintf(_T("\r\n========================================\r\n"));
				_tprintf(_T("\"?\"     -show cmd help\r\n"));
				_tprintf(_T("\"start\" -start service\r\n"));
				_tprintf(_T("\"stop\"  -stop service\r\n"));
				_tprintf(_T("\"exit\"  -exit service\r\n"));
				_tprintf(_T("========================================\r\n"));
			}
			else if (_tcsncmp(cinCmd, _T("start"), 5) == 0) 
			{
				if (!bStart) 
				{
				OnStart(0, NULL);

				_tprintf(_T("\r\n========================================\r\n"));
				_tprintf(_T("-> start service\r\n"));
				_tprintf(_T("========================================\r\n"));
				}
				bStart = true;
			}
			else if (_tcsncmp(cinCmd, _T("stop"), 4) == 0) 
			{
				if (bStart)
				{
				OnStop();

				_tprintf(_T("\n========================================\n"));
				_tprintf(_T("-> stop service\r\n"));
				_tprintf(_T("========================================\n"));
				}

				bStart = false;
			}
			else if (_tcsncmp(cinCmd, _T("exit"), 4) == 0) 
			{

				_tprintf(_T("\r\n========================================\r\n"));
				_tprintf(_T("-> exit service\r\n"));
				_tprintf(_T("========================================\r\n"));

				break;
			}
			else 
			{
				_tprintf(_T("invalid cmd\r\n"));
			}
			}

			if (bStart)
			OnStop();

			return true;
		}

		return ServiceBase::Run();//服务的方式运行
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	//创建服务对象
	CTestService service("TestService");
	
	//带有参数的调用
	if (argc > 1) 
	{
		if (_tcscmp(argv[1], _T("install")) == 0) {//安装服务
			OutputDebugString(_T("TestService:Installing service\n"));
			if (!ServiceInstaller::Install(service)) {
				OutputDebugString(_T("TestService:Couldn't install service\n"));
				return -1;
			}

			OutputDebugString(_T("TestService:Service installed\n"));
			return 0;
		}

		if (_tcscmp(argv[1], _T("uninstall")) == 0) {//卸载服务
			OutputDebugString(_T("TestService:Uninstalling service\n"));
			if (!ServiceInstaller::Uninstall(service)) {
				OutputDebugString(_T("TestService:Couldn't uninstall service: %d\n"));
				return -1;
			}

			OutputDebugString(_T("TestService:Service uninstalled\n"));
			return 0;
		}

		if (_tcscmp(argv[1], _T("console")) == 0) {//以控制台的方式运行
			OutputDebugString(_T("TestService:console running\n"));
			service.Run(_T("console"));
			return 0;
		}
	}
	else
	{//以服务的方式运行
		OutputDebugString(_T("TestService:start service"));
		service.Run();
	}	
	return 0;
}



具体的demo地址如下:创建windows系统服务demo



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值