写一个Windows上的守护进程(5)文件系统重定向
写一个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.aspx 和https://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)文件系统重定向的更多相关文章
- 写一个Windows上的守护进程(8)获取进程路径
写一个Windows上的守护进程(8)获取进程路径 要想守护某个进程,就先得知道这个进程在不在.我们假设要守护的进程只会存在一个实例(这也是绝大部分情形). 我是遍历系统上的所有进程,然后判断他们的路 ...
- 写一个Windows上的守护进程(7)捕获异常并生成dump
写一个Windows上的守护进程(7)捕获异常并生成dump 谁都不能保证自己的代码不出bug.一旦出了bug,最好是崩溃掉,这样很快就能被发现,若是不崩溃,只是业务处理错了,就麻烦了,可能很长时间之 ...
- 写一个Windows上的守护进程(6)Windows服务
写一个Windows上的守护进程(6)Windows服务 守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了. 关于Windows服务的官方文档,大家可以看https://msd ...
- 写一个Windows上的守护进程(4)日志其余
写一个Windows上的守护进程(4)日志其余 这次把和日志相关的其他东西一并说了. 一.vaformat C++日志接口通常有两种形式:流输入形式,printf形式. 我采用printf形式,因为流 ...
- 写一个Windows上的守护进程(3)句柄的管理
写一个Windows上的守护进程(3)句柄的管理 在Windows中编程,跟HANDLE打交道是家常便饭.为了防止忘记CloseHandle,我都是使用do-while-false手法: void f ...
- 写一个Windows上的守护进程(2)单例
写一个Windows上的守护进程(2)单例 上一篇的日志类的实现里有个这: class Singleton<CLoggerImpl> 看名字便知其意--单例.这是一个单例模板类. 一个进程 ...
- 写一个Windows上的守护进程(1)开篇
写一个Windows上的守护进程(1)开篇 最近由于工作需要,要写一个守护进程,主要就是要在被守护进程挂了的时候再把它启起来.说起来这个功能是比较简单的,但是我前一阵子写了好多现在回头看起来比较糟糕的 ...
- 写一个Windows服务
做了两个和Windows服务有关的项目了,最开始的时候没做过,不懂,现在明白了许多.需要注意的是,如果不想登录什么的,最后在添加安装程序的那里选择那个字长的右键属性,把启动方式改为local syst ...
- 写一个限制上传文件大小和格式的jQuery插件
在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态&quo ...
随机推荐
- js基础小总结之string&array&object
一.数据类型之间的转换 string--->number :parseInt(string)/parseFloat(string); 注:在Date中,因为返回值date为单位为ms的字符串,将 ...
- ASP.NET MVC3快速入门——第四节、添加一个模型
在本节中我们将追加一些类来管理数据库中的电影.这些类将成为我们的MVC应用程序中的“模型”部分.我们将使用一个.NET Framework的被称之为“Entiry Framework”的数据访问技术来 ...
- (\S)? 匹配0个或者一个前导字符
---------------------------------------------- "1" 模式: \"(?<id>(\S+)?)\" ? ...
- javabeans的运用
javabeans的运用 对javabean的使用我开始严重的郁闷,跟着书上说的做,但是总是不成功.后来别人说我是基础不牢靠.我觉得应该从servlet学起然后再加进入JSP学是非常快的,对于JAVA ...
- 【转】Android源码下载过程的一些注意事项
原文网址:http://www.360doc.com/content/14/0113/11/11948835_344809459.shtml 其它一些事项说明: 1.在源代码下载过程中,我们在源代码下 ...
- ofbiz ins
- JAVA获得系统配置文件的System Properties
来个java获得系统配置文件的 public class SystemProperties { public static void main(String[] args) { Properties ...
- hdu 3853 LOOPS(概率 dp 期望)
Problem Description Akemi Homura is a Mahou Shoujo (Puella Magi/Magical Girl). Homura wants to help ...
- Direct3D 2D文本绘制
现在学习下Direct3D在窗口中绘制一些文本信息,ID3DXFont接口负责创建字体和绘制二维的文本.我们介绍下ID3DXFont的用法. 1.创建LPD3DXFONT接口 LPD3DXFONT g ...
- Java并发框架——AQS堵塞队列管理(一)——自旋锁
我们知道一个线程在尝试获取锁失败后将被堵塞并增加等待队列中,它是一个如何的队列?又是如何管理此队列?这节聊聊CHL Node FIFO队列. 在谈到CHL Node FIFO队列之前,我们先分析这样 ...