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

在Windows上经常操作文件或注册表的同学可能知道,有“文件系统/注册表重定向”这么一回事。大致来说就是32位程序在64位的Windows上运行时,操作系统会把对System32文件夹的访问重定向到SysWow64下,把对HKEY_LOCAL_MACHINE\SOFTWARE的访问重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下。当然不止这些路径和注册表。详情请查看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,为什么呢?

在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

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

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

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

    写一个Windows上的守护进程(8)获取进程路径 要想守护某个进程,就先得知道这个进程在不在.我们假设要守护的进程只会存在一个实例(这也是绝大部分情形). 我是遍历系统上的所有进程,然后判断他们的路 ...

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

    写一个Windows上的守护进程(7)捕获异常并生成dump 谁都不能保证自己的代码不出bug.一旦出了bug,最好是崩溃掉,这样很快就能被发现,若是不崩溃,只是业务处理错了,就麻烦了,可能很长时间之 ...

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

    写一个Windows上的守护进程(6)Windows服务 守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了. 关于Windows服务的官方文档,大家可以看https://msd ...

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

    写一个Windows上的守护进程(4)日志其余 这次把和日志相关的其他东西一并说了. 一.vaformat C++日志接口通常有两种形式:流输入形式,printf形式. 我采用printf形式,因为流 ...

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

    写一个Windows上的守护进程(3)句柄的管理 在Windows中编程,跟HANDLE打交道是家常便饭.为了防止忘记CloseHandle,我都是使用do-while-false手法: void f ...

  6. 写一个Windows上的守护进程(2)单例

    写一个Windows上的守护进程(2)单例 上一篇的日志类的实现里有个这: class Singleton<CLoggerImpl> 看名字便知其意--单例.这是一个单例模板类. 一个进程 ...

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

    写一个Windows上的守护进程(1)开篇 最近由于工作需要,要写一个守护进程,主要就是要在被守护进程挂了的时候再把它启起来.说起来这个功能是比较简单的,但是我前一阵子写了好多现在回头看起来比较糟糕的 ...

  8. 写一个Windows服务

    做了两个和Windows服务有关的项目了,最开始的时候没做过,不懂,现在明白了许多.需要注意的是,如果不想登录什么的,最后在添加安装程序的那里选择那个字长的右键属性,把启动方式改为local syst ...

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

    在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态&quo ...

随机推荐

  1. 学习CAS实现SSO单点登录

    学习CAS实现SSO单点登录 网上找了几篇比较详细的教程,在这记录一下: 原理: CAS实现SSO单点登录原理 教程: 1.CAS实现单点登录(SSO)经典完整教程 2.SSO之CAS单点登录实例演示 ...

  2. JSP EL

    一.JSP EL语言定义 E L(Expression Language)  目的:为了使JSP写起来更加简单. 表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 ...

  3. 动态编译添加php模块

    注意:转载请注明出处:http://www.programfish.com/blog/?p=85 在很多时候我们用linux里搭建web服务器的时候会需要编译安装php套件,而在编译安装后可能又会需要 ...

  4. Mysqldump记录

    MySql导出特定的一段记录(导出为SQL语句) mysqldump –u root -p 数据库名 表名 --where=" author like '%Joking%' " & ...

  5. OC和C语言的混编注意点和好处

    苹果的Objective-C编译器批准用户在统一个源文件里自由地混杂利用C++和Objective-C,混编后的语言叫Objective-C++.有了它,你就能够在Objective-C利用过程中利用 ...

  6. [Ioi2007]Miners 矿工配餐(BZOJ1806)

    [Ioi2007]Miners 矿工配餐 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 214  Solved: 128 Description 现有两 ...

  7. rsyslog VS syslog-ng,日志记录哪家强?

    还有慢慢摸索,NG的MYSQL配置,我始终没搞好. RSYSLOG则比较容易. 另外,也可以每个RSYSLOG直接入库,不需要经过LOG SERVER..如果有一个大内网的话... 配合LOGANAL ...

  8. Thinkphp将中文年份转换为数字年份的问题

    今天遇到一个问题:想将中文年份转换为数字年份,例如:"二零一六"-->'2016'. 在网上搜了一下,没找到可以直接处理的函数(也许是我搜索信息的能力有限吧>_< ...

  9. 笔试、面试重点总结:WIN32、MFC与Linux

    win32 1. Win32应用程序的基本类型. 2. 创建win32窗口程序的几个步骤,及使用到的函数. 3. nmake 与 makefile. 4. 有哪些字符集? Win32对于各种字符集如何 ...

  10. ios block使用

    BlockTest.h #import <Foundation/Foundation.h> typedef void (^didFinishBlock)(NSString *str); t ...