写一个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函数的流程是这样的:

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日星期六

时间: 2024-12-27 18:29:27

写一个Windows上的守护进程(6)Windows服务的相关文章

写一个Windows上的守护进程(5)文件系统重定向

写一个Windows上的守护进程(5)文件系统重定向 在Windows上经常操作文件或注册表的同学可能知道,有“文件系统/注册表重定向”这么一回事.大致来说就是32位程序在64位的Windows上运行时,操作系统会把对System32文件夹的访问重定向到SysWow64下,把对HKEY_LOCAL_MACHINE\SOFTWARE的访问重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下.当然不止这些路径和注册表.详情请查看MSDN:https://msdn.

写一个Windows上的守护进程(1)开篇

写一个Windows上的守护进程(1)开篇 最近由于工作需要,要写一个守护进程,主要就是要在被守护进程挂了的时候再把它启起来.说起来这个功能是比较简单的,但是我前一阵子写了好多现在回头看起来比较糟糕的代码,所以就想这次写的顺眼一点.写完后发现,诶,还可以哟.于是就总结总结. 一.大致需求 1. 功能——当被守护进程挂掉后再次启动它 2. 可配置需要守护的进程 二.通盘考虑 1. 为了避免重复造轮子,况且有的轮子可能自己也造不出来,上boost库 2. 为了能够获得较高的权限和能够开机自动启动,将

写一个Windows上的守护进程(4)日志其余

写一个Windows上的守护进程(4)日志其余 这次把和日志相关的其他东西一并说了. 一.vaformat C++日志接口通常有两种形式:流输入形式,printf形式. 我采用printf形式,因为流输入不好控制格式. printf形式要求日志接口支持不定长参数,我没有直接在日志实现类里边支持不定长参数,而是只接受一个字符串参数,可以参见第一篇. 为什么呢? 如果要成为不定长参数,就是这样 bool log_string(const LOG_LEVEL level, const char* fi

写一个Windows上的守护进程(3)句柄的管理

写一个Windows上的守护进程(3)句柄的管理 在Windows中编程,跟HANDLE打交道是家常便饭.为了防止忘记CloseHandle,我都是使用do-while-false手法: void f() { HANDLE h = NULL; do { } while (false); if (h) { CloseHandle(h); h = NULL; } } HANDLE一多,就得写好几段长得一样的清理代码,比较麻烦.仔细一想,这个其实很容易写一个关闭器——在出作用域时自动关闭: class

写一个Windows上的守护进程(8)获取进程路径

写一个Windows上的守护进程(8)获取进程路径 要想守护某个进程,就先得知道这个进程在不在.我们假设要守护的进程只会存在一个实例(这也是绝大部分情形). 我是遍历系统上的所有进程,然后判断他们的路径和要守护的进程是否一致,以此来确定进程是否存在. 遍历进程大家都知道用CreateToolhelp32Snapshot系列API,但是他们最后取得的是进程exe名称,不是全路径,如果仅依靠名称就可以达到目的也就罢了,但是有的时候还是得取到全路径,这样会更靠谱一些. 那么问题来了,如何取到进程全路径

写一个Windows上的守护进程(7)捕获异常并生成dump

写一个Windows上的守护进程(7)捕获异常并生成dump 谁都不能保证自己的代码不出bug.一旦出了bug,最好是崩溃掉,这样很快就能被发现,若是不崩溃,只是业务处理错了,就麻烦了,可能很长时间之后才能被发现. 那么如果崩溃掉,怎么查错呢? 写过Windows驱动的同学应该知道,一旦崩溃,系统会生成dump文件,然后就可以根据dump文件.pdb文件.源码用windbg分析了.应用层的程序同样可以在崩溃的时候生成dump文件,只是没人帮你完成这个步骤,得自己动手. 1. API 这里涉及到的

写一个限制上传文件大小和格式的jQuery插件

在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态"的jQuery插件,使之能限制上传文件的大小和格式. 首先,写一个名称为checkFileTypeAndSize.js的插件.通过判断当前文件的后缀名是否被包含在预先设置所允许的后缀名数组中,来限制文件格式:通过判断当前文件在IE以及其它浏览器下的尺寸是否大于预先设置所允许的最大文件尺寸,来限制文件大小:

枚举Windows系统服务,通过进程ID取服务名

 //枚举Windows系统服务 //使用到的函数以及MSDN的说明如下: //1.OpenSCManager说明 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx //2.EnumServicesStatusEx说明 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms682640(v=vs.85).aspx /

linux shell脚本守护进程监控svn服务

最近搭建的svn服务不知道什么原因服务总是被关闭(如果你不知道怎么搭建svn可以参考linux下搭建svn版本控制软件),因此用shell脚本实现一个守护进程.用于监控svn服务是否启动,如果服务不在则启动. 创建监控脚本svnmonit.sh #! /bin/sh #进程名字可修改 PRO_NAME=svnserve PORT=58652 REP_DIR=/www/svndata while true ; do #用ps获取$PRO_NAME进程数量 NUM=`ps aux | grep ${