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


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

    在Windows上经常操作文件或注册表的同学可能知道,有“文件系统/注册表重定向”这么一回事。大致来说就是32位程序在64位的Windows上运行时,操作系统会把对System32文件夹的访问重定向到SysWow64下,把对HKEY_LOCAL_MACHINESOFTWARE的访问重定向到HKEY_LOCAL_MACHINESOFTWAREWow6432Node下。当然不止这些路径和注册表。详情请查看MSDN:https://msdn.microsoft.com/en-us/library/aa384187.aspxhttps://msdn.microsoft.com/en-us/library/aa384232(v=vs.85).aspx

    我们通常为了方便发布,都只会编译一份32位的程序,不会编译64的程序。如果代码中涉及到了访问文件和注册表,那就要考虑这个问题了。

    Windows提供了两个API(准确的说是3个,但其中一个已不推荐使用了)来禁用文件系统/注册表重定向:Wow64DisableWow64FsRedirection,Wow64RevertWow64FsRedirection。顾名思义,前者用于禁用,后者用于恢复。

    有的同学可能就说了:我一直禁用就行了,干嘛还要恢复?这你就想少了,有的代码段可能并不关心是否有重定向,所以并没有考虑这个问题,假若这个代码段的结果会影响多个线程,而你刚好又把包含这个代码段的某一线程的重定向禁用了,那就成了有的线程禁用了重定向,有的线程没有禁用,获取的结果就不一致了。

    一般我们在会有重定向问题的函数调用前禁用重定向,调用完毕后,恢复重定向。这很容易让人想到使用RAII手法:在类构造函数中禁用,析构函数中恢复:

    class scoped_disable_wow64_fsredirection : public boost::noncopyable
    {
    public:
        scoped_disable_wow64_fsredirection();
    
        ~scoped_disable_wow64_fsredirection();
    
    private:
        static bool disable(void **ppOldValue);
        static bool revert(void *pOldValue);
    
    private:
        void *_pOldValue;
    };

    构造函数的实现中调用了disable,析构函数的实现中调用了revert。

    其中disable就是调用了Wow64DisableWow64FsRedirection,revert就是调用了Wow64RevertWow64FsRedirection。

    注:类名字这么长是为了能够达到“顾名思义”的程度。还没想到什么更好的名字。哎,起名真是个头疼的事。

    但是我们不能直接调用这两个Windows API,为什么呢?

    wow64_thumb11

    在WindowsXP这么流行的平台上,它们没有啊!你要是直接调用这两个函数,在XP上,整个程序就跑不起来。

    怎么办?

    用GetProcAddress动态加载这两个函数,如果没有这两个函数,就说明不存在重定向问题。

    以下是实现:

    static boost::once_flag once_ = BOOST_ONCE_INIT;
    
    
    typedef int (__stdcall *fnWow64DisableWow64FsRedirection)(void *);
    typedef int (__stdcall *fnWow64RevertWow64FsRedirection)(void *);
    
    static fnWow64DisableWow64FsRedirection g_fnWow64DisableWow64FsRedirection = NULL;
    static fnWow64RevertWow64FsRedirection g_fnWow64RevertWow64FsRedirection = NULL;
    
    
    static void load_wow64_funcs()
    {
        g_fnWow64DisableWow64FsRedirection = reinterpret_cast<fnWow64DisableWow64FsRedirection>
            (WindowsUtil::load_function("Kernel32.dll", "Wow64DisableWow64FsRedirection"));
    
        g_fnWow64RevertWow64FsRedirection = reinterpret_cast<fnWow64RevertWow64FsRedirection>
            (WindowsUtil::load_function("Kernel32.dll", "Wow64RevertWow64FsRedirection"));
    }
    
    
    
    scoped_disable_wow64_fsredirection::scoped_disable_wow64_fsredirection()
        : _pOldValue(NULL)
    {
        boost::call_once(once_, load_wow64_funcs);
        disable(&_pOldValue);
    }
    
    scoped_disable_wow64_fsredirection::~scoped_disable_wow64_fsredirection()
    {
        revert(_pOldValue);
    }
    
    
    bool scoped_disable_wow64_fsredirection::disable(void **ppOldValue)
    {
        bool ret = true;
    
        if (g_fnWow64DisableWow64FsRedirection)
        {
            if (!g_fnWow64DisableWow64FsRedirection(ppOldValue))
            {
                ErrorLogLastErr("Wow64DisableWow64FsRedirection fail");
                ret = false;
            }
        }
    
        return ret;
    }
    
    bool scoped_disable_wow64_fsredirection::revert(void *pOldValue)
    {
        bool ret = true;
    
        if (g_fnWow64RevertWow64FsRedirection)
        {
            if (!g_fnWow64RevertWow64FsRedirection(pOldValue))
            {
                ErrorLogLastErr("Wow64RevertWow64FsRedirection fail");
                ret = false;
            }
        }
    
        return ret;
    }

    这里用了前面文章讲到的call_once去加载这两个函数。

    load_function封装了GetModuleHandleA- GetProcAddress两个函数的调用,详情请参看源码。

    使用时,仅需定义一个类实例就可以了。切记,要尽量缩小作用域,以免影响其他代码段。

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

    2015年11月1日星期日


     

    **********************************************************************

    更新记录:

    【2015年11月6日 星期五】set boost::once_flag instance init value to BOOST_ONCE_INIT

    **********************************************************************

  • 相关阅读:
    2.5亿!华为成立新公司!
    两年半换第 4 份工作,做个总结
    不懂什么叫编程?
    Google 为什么把几十亿行代码放在一个库?
    IntelliJ 平台 2020 年路线图
    别找了,这是 Pandas 最详细教程了
    MongoDB是什么?看完你就知道了!
    有了这个神器,轻松用 Python 写 APP !
    整理出来几个比较实用的代码对比工具
    学习进度条 第六十一-七十五天 SpringMVC学习笔记
  • 原文地址:https://www.cnblogs.com/mkdym/p/4928535.html
Copyright © 2020-2023  润新知