zoukankan      html  css  js  c++  java
  • 写一个Windows上的守护进程(6)Windows服务

    写一个Windows上的守护进程(6)Windows服务

    守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了。

    关于Windows服务的官方文档,大家可以看https://msdn.microsoft.com/en-us/library/windows/desktop/ms686953(v=vs.85).aspx

    总的来说,服务的行为区别于普通应用程序的地方有以下几点:

    1. 一般来说,服务是运行于System用户下的,当然也可以自己指定。也就是说服务可以在无用户登录的情况下运行

    2. 一般来说,服务是没有用户交互的

    3. 服务可以通过服务管理器管理(启动、停止等等)

    服务程序的编写区别于普通应用程序的地方有以下几点:

    1. 服务程序执行的主体是ServiceMain函数,你需要在main函数中调用StartServiceCtrlDispatcher注册你的ServiceMain函数。也就是说你的功能代码应写在你的ServiceMain函数中。StartServiceCtrlDispatcher直到服务停止才会返回

    2. 在ServiceMain函数中,你要及时向服务管理器报告自己的状态,好让其向用户展示自己。一般来说,ServiceMain函数的流程是这样的:

    clip_image002

    3. 作为一个正常的服务,要响应用户的请求,比如停止,这要调用RegisterServiceCtrlHandler注册自己的处理函数,也可以处理自定义的control code;要响应系统请求,比如关机,这要调用SetConsoleCtrlHandler注册自己的处理函数

    4. 要想自己的程序成为服务,还得向Windows注册,这要调用CreateService,还有停止服务,启动服务之类的,详情参看MSDN,上面都有例子

    我把这些步骤封装了下,做成了一个单例类——很显然,单例很适用,一个进程对应一个服务——CWin32Service:

    class CWin32Service : public Singleton<CWin32Service>
    {
        friend class Singleton<CWin32Service>;
    
    private:
        CWin32Service(void);
    
    public:
        ~CWin32Service(void);
    
    public:
        bool init(const ServiceInfo& info);
    
        typedef std::vector<tstring> ArgList;
        typedef boost::function<bool(const ArgList&)> StartingFunction;//ArgList是应用程序的命令行参数
        typedef boost::function<void(const ArgList&)> ServiceFunction;
    
        void register_starting_function(const StartingFunction& f);
        void register_running_function(const ServiceFunction& f);
        void register_control_code_function(const DWORD c, const ServiceFunction& f);
    
        bool go();
    };

    上面是其主要对外接口,接口基本上对应于上面ServiceMain的流程:

    l Init:告知服务名称等信息

    l StartingFunction:通常都是做好初始化工作,加载配置什么的,然后启动工作线程

    l running_function:通常都是等待退出信号,等待工作线程结束什么的。此函数返回意味着服务结束了

    l control_code_function:这是要处理的控制代码。一般我们都会要处理stop control code的,不过你不设置也没关系

    l go:这个就是调用StartServiceCtrlDispatcher注册ServiceMain函数。直到服务结束才会返回。ServiceMain中就是按流程调用上面注册的函数和报告状态

    当然我们不能只完成服务程序的编写,还要让用户能安装服务、启动服务、通知服务、卸载服务的方法,我把这些提供在了命令行里:

    l DaemonSvc.exe –intsall

    l DaemonSvc.exe –start

    l DaemonSvc.exe –stop

    l DaemonSvc.exe –remove

    这些动作的实现都被我拎出来放到了ServiceUtil里面。

    服务是没办法在VC里直接按F5调试的,你那样启动起来的不是服务,是普通应用程序。为了方便调试,我把服务分了两种模式:普通模式,服务模式:不带参数启动起来的是普通模式,带-svc参数起来的是服务模式(所以你会看到我实现服务安装的时候,给的命令行后面是有-svc的)。普通模式下,不走StartServiceCtrlDispatcher,直接ServiceMain,并且不和服务管理器打交道。

    普通模式开发调试完毕后,可以安装启动服务,看下服务模式下是不是正常,此时也可以用VC附加到服务进程上调试。

    服务类的使用示例:

    int main(int argc, char * argv[])
    {
        InitLog("", 0, LOG_DEBUG);
        //try to enable debug privilege for querying other processes' info
        WindowsUtil::set_privilege(SE_DEBUG_NAME, true);
    
        ServiceInfo si;
        si.name = TSTR("DaemonSvc");
        si.display_name = si.name;
    
        CWin32Service& svc = CWin32Service::get_instance_ref();
    
        if (!svc.init(si))
        {
            ErrorLog("init service fail");
        }
        else
        {
            InfoLog("init service success");
    
            svc.register_starting_function(starting);
            svc.register_running_function(running);
            svc.register_control_code_function(SERVICE_CONTROL_STOP, stopping);
            svc.register_control_code_function(200, restart);
    
            if (!svc.go())
            {
                ErrorLog("make service go fail");
            }
            else
            {
                InfoLog("everything is OK");
            }
        }
    
        return 0;
    }

    源码:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。

    2015年11月7日星期六

  • 相关阅读:
    noip欢乐赛10.24 分火腿
    noip2014 无线网络发射器选址/wireless.
    noip2012 借教室 线段树最小值做法
    Codevs1021题解---SPFA+路径记录
    Vijos1448题解---线段树+括号法
    Vijos1425题解---栈
    Codevs1022题解---匈牙利算法
    人们总要为曾经的年轻买单
    2017-10-26
    2017-10-24LCA
  • 原文地址:https://www.cnblogs.com/mkdym/p/4945606.html
Copyright © 2011-2022 走看看