写一个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 ...
随机推荐
- ESP8266固件修改可以控制多个IO方法
之前在论坛上找到了一个通过ESP8266可以控制GPIO0的固件和app,但是自己做的家庭影音灯光系统是需要控制多个IO从而控制STM32.通过观看大明的视频,了解了GPIO的控制方法. 在固件的ap ...
- HBase -ROOT-和.META.表结构(region定位原理)
在HBase中,大部分的操作都是在RegionServer完成的,Client端想要插入,删除,查询数据都需要先找到相应的RegionServer.什么叫相应的RegionServer?就是管理你要操 ...
- 【5】python核心编程 第八章-条件和循环
1.=== range() 的完整语法=== Python 提供了两种不同的方法来调用 range() . 完整语法要求提供两个或三个整数参数: range(start, end, step =1) ...
- chart画图
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- 在fedora 20下使用ssh server
在红帽和centos下,一般安装完后会自带ssh,然后可以通过/etc/init.d/sshd start的方式运行,但是在Fedora 20下,系统改用了另外一套服务开启机制. 首先安装ssh se ...
- Flash型单片机的加密与解密
厂商利用单片机进行产品开发时,都会关心其代码和数据的保密性.考虑到用户在编写和调试代码时所付出的时间和精力,代码的成本是不言而喻的. 早期的单片机,代码是交给芯片制造商制成掩膜ROM.有两种加密 ...
- 时间类处理<1>
2016/05/31 14:47:21 [emerg] 14629#0: location "/nginx_status" is outside location "/p ...
- JS代码的window.location属性详解
转载:http://www.5icool.org/a/201105/a564.html 如果你稍微懂一些JS代码,一般都会知道 window.location.href 这个属性.并且用该属性获取页面 ...
- jdbc连接数据库工具类
import java.lang.reflect.Field; import java.sql.Connection; import java.sql.DriverManager; import ja ...
- POJ3669(Meteor Shower)(bfs求最短路)
Meteor Shower Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12642 Accepted: 3414 De ...