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

Posted 小哈龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;


————————————————
版权声明:本文为CSDN博主「萧戈」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaoyafang123/article/details/52235295

以上是关于C/C++ 创建windows系统服务程序的主要内容,如果未能解决你的问题,请参考以下文章

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

用C/C++创建windows服务程序

用C/C++创建windows服务程序

用C/C++创建windows服务程序

Socket 服务器应用程序的选择:C/C++ 或 C#

C/C++打造Windows项目教程:任务管理器(Windows操作系统)!手把手带你进行Windows服务器开发