写一个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 ...
随机推荐
- 深入理解Azure自动扩展集VMSS(3)
在实际使用过程当中,使用VMSS有一些最佳实践的建议和限制,便于你在做自动扩展设计的时候进行考虑: 关于VMSS 如果你使用的是系统镜像,一个扩展集中虚拟机数量不能超过100 无论是在ASM还是ARM ...
- python描述符 descriptor
descriptor 在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor.descriptor通常用来改 ...
- Android原理View、ViewGroup
Android的UI界面都是由View和ViewGroup及其派生类组合而成的.其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的.Andro ...
- Bootstrap 实例 - 模态框(Modal)插件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- MyEclipse安装Eclipse Memory Analyzer插件,并进行错误文件分析流程
在看深入JVM虚拟机一书(p50,2.4 实战OutOfMemoryError),有一个Java堆溢出的例子,使用到了Eclipse Memory Analyzer插件,由于自己现在使用的是MyEcl ...
- IOS 项目问题总结
把自己项目中遇到的问题总结一下,供大家参考,希望大家多多提出意见!! 在Xcode 6.2中遇到Your build settings specify a provisioning profile w ...
- poj3294 --Life Forms
Life Forms Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 12483 Accepted: 3501 Descr ...
- CMD获取当前目录的绝对路径 (转)
@echo offecho 当前盘符:%~d0echo 当前盘符和路径:%~dp0echo 当前批处理全路径:%~f0echo 当前盘符和路径的短文件名格式:%~sdp0echo 当前CMD默认目录: ...
- 微信开放JS-SDK,助力网页开发
From:http://mp.weixin.qq.com/s?__biz=MjM5NDAwMTA2MA==&mid=209208141&idx=1&sn=1f075212b34 ...
- LLVM安装
cd ~tar -vzxf llvm-3.3.src.tar.gzmv llvm-3.3.src llvmcd llvm/tools/tar -vzxf cfe-3.3.src.tar.gzmv cf ...