写一个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 closer : public boost::noncopyable
{
public:
    closer(HANDLE h)
        : h_(h)
    {

    }

    ~closer()
    {
        if (h_)
        {
            CloseHandle(h_);
            h_ = NULL;
        }
    }

private:
    HANDLE h_;
};

创建或打开一个句柄后,将其关闭动作委托给closer。

这是我在写这篇文章的时候想到的,写代码的时候我用的是另一个办法:scoped_handle。这是从boost里边那些运用RAII手法的类上学到得,比如scoped_ptr,scoped_array,名字也是学过来的:

template<HANDLE invalid_value = NULL>
class scoped_handle : public boost::noncopyable
{
public:
    scoped_handle()
        : h_(invalid_value)
    {
    }

    scoped_handle(const HANDLE &h)
        : h_(h)
    {
    }

    ~scoped_handle()
    {
        destory();
    }

    //you should ensure not self-assignment
    void reset(const HANDLE &h)
    {
        destory();
        h_ = h;
    }

    void destory()
    {
        if (h_ != invalid_value)
        {
            //CloseHandle will set last error code
            //so we should recover it
            //someone may use reset(CreateFile(...))
            last_error_recover r;

            CloseHandle(h_);
            h_ = invalid_value;
        }
    }

    bool valid() const
    {
        return h_ != invalid_value;
    }

    HANDLE& get_ref()
    {
        return h_;
    }

    HANDLE* get_ptr()
    {
        return &h_;
    }

private:
    HANDLE h_;
};

有一个模板参数invalid_value,这是句柄的无效值,我现在见到的有两种:NULL,INVALID_HANDLE_VALUE。

用法举例:

scoped_handle<> hProcess(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
if (!hProcess.valid())
{
    ErrorLogLastErr("OpenProcess[%lu] fail", pid);
}
else
{
    s = query(hProcess.get_ref(), native_name);
}
scoped_handle<> hToken;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken.get_ptr()))
{
    //...
}
scoped_handle<> m_exit_event;
m_exit_event.reset(CreateEvent(NULL, TRUE, FALSE, NULL));
if (!m_exit_event.valid())
{
    //...
}

我现在为自己找了一个不使用closer的理由:我不喜欢让别人帮助我做力所能及的事。closer就像是某人new了一个对象,然后把指针给了另一个人,让他去“擦屁股”,然而自己“擦屁股”不过是举手之劳。不过,这个理由不强劲,closer的便利性远远大于对“擦屁股”的厌恶。

这里有一个todo:我希望能定制Close动作,就是Close动作作为一个模板参数,这样一来,我就可以把HANDLE也提到模板参数的位置上,这个东西的适用范围就更广了,但是我不知道如何把Close动作提到模板参数的位置上。

想必眼尖的同学看到了上面的代码里有个last_error_recover,这是一个很简单的类,注释已经说明了它的用途,下面是实现:

class last_error_recover : public boost::noncopyable
{
public:
    last_error_recover()
        : code_(GetLastError())
    {
    }

    last_error_recover(const DWORD code)
        : code_(code)
    {
    }

    ~last_error_recover()
    {
        SetLastError(code_);
    }

private:
    const DWORD code_;
};

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

2015年10月31日星期六

时间: 2024-08-03 09:02:52

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

写一个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上的守护进程(6)Windows服务

写一个Windows上的守护进程(6)Windows服务 守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了. 关于Windows服务的官方文档,大家可以看https://msdn.microsoft.com/en-us/library/windows/desktop/ms686953(v=vs.85).aspx. 总的来说,服务的行为区别于普通应用程序的地方有以下几点: 1. 一般来说,服务是运行于System用户下的,当然也可以自己指定.也就是说服务可以在无用户登录的情

写一个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以及其它浏览器下的尺寸是否大于预先设置所允许的最大文件尺寸,来限制文件大小:

Python写自动化之写一个Windows 服务

Python 写windows 服务,需要使用 pywin32包. 直接上代码: #encoding=utf8 ''' Created on 2014-7-1 @author: wangmengnan ''' import os import sys import win32serviceutil import win32service import win32event class PythonService(win32serviceutil.ServiceFramework): #服务名 _

一个极简的守护进程Bash脚本

由于最近写的Node.js程序因为一些Bug,会出现一些自动退出的问题,所以需要在它退出的时候及时发现,并重新启动 于是查阅了些资料,写了一个Bash的程序,功能十分简单,就是每隔3s判断一次处在6000端口的node程序是否正在运行,如果没有发现6000端口,那么执行启动程序的命令,已经使用了一段时间,感觉效果蛮好的,简单小巧,安全可靠,记录一下,以便以后使用. #!/bin/bash BASEDIR='/server' while true; do PRO_NOW=`netstat -tun