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