写一个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 ...
随机推荐
- ANSI与UINCODE编码
简要说明: ANSI是一种字符代码,为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符. Uincode(统一码.万国码.单一码)是计算机科学领域里的一项业界标 ...
- C++中cin输入类型不匹配解决方法
#include <iostream> #include <set> using namespace std; int main() { int a; cin>>a ...
- 洛谷 P1066 2^k进制数
P1066 2^k进制数 题目描述 设r是个2^k 进制数,并满足以下条件: (1)r至少是个2位的2^k 进制数. (2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位. ( ...
- set用法总结
set集合用于存放不重复的元素 template <class Key, class Compare = less<Key>, class Alloc = alloc> cla ...
- beini系列_2_beini装入虚拟机
- DataTables选择行并删除(删除单行)
$(document).ready(function() { var table = $('#example').DataTable(); $('#example tbody').on( 'click ...
- Winform(C#)限制程序只运行一个实例
C#控制只运行开启一个程序 在这个例子中不需要调用ReleaseMutex,mutex会在程序结束时自动释放.为了防止mutex过早释放,在程序的最后调用下GC.KeepAlive (mutex). ...
- Win8.1 MSDN各版本下载(64位/32位,简体中文,繁体中文,英文),X86&X64,EN,CHS,CHT
英文64位ed2k://|file|en_windows_8_1_x64_dvd_2707217.iso|3899295744|8E604054013D21209B851E41DC19F6F5|/ 英 ...
- 索引列上的统计 <第一篇>
一.索引在查询优化中的角色 SQL Server的查询优化器是基于开销的优化器.它通过确认选择性.数据的唯一性以及过滤数据(通过WHERE或JOIN子句)所使用的列来决定最佳的数据访问机制.统计与索引 ...
- Smallest Rectangle Enclosing Black Pixels 解答
Question An image is represented by a binary matrix with 0 as a white pixel and 1 as a black pixel. ...