32bit程序在64bit操作系统下处理重定向细节
1. 64bit操作系统的重定向机制以及目的
在64bit操作系统中,为了无缝兼容32bit程序的运行,64bit的Windows操作系统采用重定向机制。目的是为了能让32bit程序在 64bit的操作系统不仅能操作关键文件文夹和关键的注册表并且又要避免与64bit程序冲突。
微软采用重定向机制的原理很简单,说白了就是 让关键文件/文件夹或者关键注册表有2个副本。 1个副本是给32bit程序访问,一个副本给64bit程序访问。
PS: 以上不是用专业术语解释,只是用白话解释。如果觉得不够专业,可以查阅详细资料
2. 64bit操作系统是如何控制32bit和64bit程序访问对应各自的副本?
这个问题,就是重定向机制的核心功能。
例子: 32bit程序在64bit Windows操作系统,要访问system32目录。
在正常的情况下: 64bit Windows操作系统的重定向机制 会在内部 把 system32目录 转向 syswow64目录, 因此32bit程序对system32目录的操作,实际是对syswow64目录进行操作。
以为由于重定向的干预,因为可以这么认为 system32目录是供给64bit程序使用的,而syswow64目录是给32bit程序使用的
代码例子: 32bit程序中有一个代码
deletefile('c:/windows/system32/a.txt') ;
这个代码在64bit Windows操作系统中,它只会删除syswow64目录中的a.txt文件,而不会删除system32目录的a.txt文件。 这就是重定向的干预结果。
3. 那32bit程序要真正访问64bit程序的system32目录要如何做呢?
微软提供一套API,可以做到上面的要求。 通过 Wow64DisableWow64FsRedirection 和 Wow64RevertWow64FsRedirection API 来配合使用
代码例子: 32bit程序中有一个代码
Wow64DisableWow64FsRedirection // 关闭重定向
deletefile('c:/windows/system32/a.txt') ;
Wow64RevertWow64FsRedirection // 恢复重定向
以上代码,就有效的删除了system32目录里面的a.txt文件,而不是syswow64目录中的a.txt文件。
4. 32bit程序在64bit操作系统下不关闭重定向功能,在全盘枚举系统目录时,是否可以同时把32bit和64bit的目录都枚举出来?
经过我测试,无法同时枚举出来,只能做2次重复扫描,第一次在打开重定向枚举出32bit的目录,然后第二次关闭重定向,在枚举64bit的目录。
6. 既然文件目录有重定向,那么注册表有没有重定向之后的副本?
有, 跟文件重定向一样。
示例: 32bit程序在64bitWindows操作系统正常访问'HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID'
在重定向的干预下,32bit程序被重定向访问到'HKEY_LOCAL_MACHINE/SOFTWARE/Classes/Wow6432node /CLSID'
如果要真正访问'HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID' ,请按照 第3点的 代码示例来做。
7. 32bit程序在64bit操作系统下不关闭重定向功能,枚举系统注册表,是否可以同时把32bit和64bit的注册信息同时枚举出来?
经过我测试,无法同时枚举出来,只能做2次重复扫描,第一次在打开重定向枚举出32bit的注册表信息,然后第二次关闭重定向,在枚举64bit的注册表信息。
8. 32bit程序在64bit操作系统下不关闭重定向功能,直接硬编码访问32bit副本的关键目录和关键注册表是否可行?
经过我测试,是可以的。
代码例子:
deletefile('c:/windows/syswow64/a.txt') ;
以上代码就是可以直接删除syswow64目录下的a.txt文件.
同理,注册表访问也一样适用.
1> Delphi程序的TRegistry类无法对KEY_WOW64_64KEY 兼容。也就是说,TRegistry.Create(KEY_WOW64_64KEY or KEY_ALL_ACCESS) 这样是无效的。
2> SHDeleteKey这类Shellapi 对 KEY_WOW64_64KEY 不兼容。也就是说就算你关闭重定位机制,也无法对64bit的注册表操作。一律全部重定位到Wow64xxx 的位置。
这是因为这类API没有KEY_WOW64_64KEY这个标志为的设置
3> 针对RegDeleteKeyEx这类带有Ex的注册表函数,不管你是否显示关闭和打开重定位机制,只要在参数设置KEY_WOW64_64KEY 或者 KEY_WOW32_32KEY 就可以直接操作 64BIT 或者 32BIT的注册表了。
下面来个代码例子 32bit程序无缝兼容于64bit操作系统, 同时删除32bit和64bit的注册表项HKEY_LOCAL_MACHINE/software/aaaa [delphi伪代码]
if IsWow64 then
begin // 如果是在64bit环境下运行32bit程序
// 1 关闭从定向
Wow64DisableWow64FsRedirection
// 删除64bit的注册表信息 HKEY_LOCAL_MACHINE/software/aaaa 的项,包括子项
RegOpenKeyEx(HKEY_LOCAL_MACHINE, software/aaaa , 0, KEY_WOW64_64KEY or KEY_ALL_ACCESS, hkey_Opend) ;
// 执行删除
RegDeleteTree(hkey_Opend, nil) ;
RegDeleteKeyEx(HKEY_LOCAL_MACHINE, software/aaaa, KEY_WOW64_64KEY or KEY_ALL_ACCESS, 0) ;
RegCloseKey(hkey_Opend) ;
// 2 恢复重定向
Wow64RevertWow64FsRedirection
end ;
// 下面开始执行32bit的删除
Reg_Operate := TRegistry.Create() ;
Reg_Operate.RootKey := HKEY_LOCAL_MACHINE ;
Reg_Operate.OpenKey('software/aaaa', False) ;
Reg_Operate.DeleteKey('/software/aaaa') ;
Reg_Operate.CloseKey() ;
管理员身份运,普通用户,WIN7下读写注册表,WIN7对普通用户可能做了特殊处理,如果读写local_machine的键,会自动跳转到current_user下。
这人遇到这个问题
“http://blog.csdn.net/huzgd/article/details/6314489”
其他:
1.KEY_WOW64_64KEY 和 KEY_WOW64_32KEY
64位的windows系统的注册表分为32 位注册表项和64位注册表项两部分。在64bit系统下,通过regedit中查看到指定路径下的注册表项均为64位注册表项,而32位注册表项被重定位到了WOW6432Node。比如HEKY_CLASSES_ROOT\CLSID是64位的注册表项,而HEKY_CLASES_ROOT\WOW6432Node\CLSID则是32位的。当然,像HEKY_CURRENT_USER下的都是shared项,不区分32位还是64位。具体可以从msdn中查到:http://msdn.microsoft.com/en-us/library/ms724072(v=VS.85).aspx 。
当然微软考虑到了这样的改变对现有软件的影响,因为64位系统上既可以跑64位的程序,也可以跑32位的。比如我在代码中对注册表的读操作::RegOpenKeyExA(HKEY_CLASSES_ROOT, regSubKeyName, 0, KEY_ALL_ACCESS, &hKey),程序是在32位的windows xp系统下用vs2005编译的,放到64位系统上则读的WOW6432Node下的项。如果需要读取64位下的项,则需要显示的指定,即::RegOpenKeyExA(HKEY_CLASSES_ROOT, regSubKeyName, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hKey)。当然这里也可以加上KEY_WOW64_32KEY,这种情况下跟没有指定是一样的。
2. 判断系统是否是64位
调试过程中过了误区,以为需要显示的指定是64位还是32位的注册表项,这样就面临一个问题:怎么判断系统是64位还是32位,在网上找了几种方法,这里也做个总结:
1)通过sizeof(int)或者sizeof(void*)的值是4还是8来判断。
这种属于屁股决定脑袋的方法,但是网上一查还真是有不少人给这样的答案。首先,int类型在64位的机器上也是占4个字节,而且这个完全依赖于编译器的实现,编译器可以用32位也可以用64位来表示整型数据。其次,sizeof操作是编译期间决定的,不是动态获取的,也就是说这个值依赖于你的编译环境,如果是在32位的系统上编译,不管跑到什么系统上去执行程序,总是会返回4.
2)通过宏#if defined(_WIN64)来判断
这个与上面的一样,也是完全依赖于编译环境。
3) 根据GetSystemInfo的结果来判断
通常代码如下
SYSTEM_INFO si;
GetSystemInfo(&si);
if((si.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_IA64)
|| (si.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64)
//64 bit;
else
//32 bit.
本以为这段代码可以work,但是亲自在intel cpu上安装了64位win7的机器上测试不成功,返回值是0,即PROCESSOR_ARCHITECTURE_INTEL
4) 根据环境变量PROCESSOR_ARCHITECTURE来判断
if(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE").IndexOf("64"))
//64-bit
else
//32-bit
这个破天荒执行成功了,查了一下64位win7的系统环境变量,该变量值为AMD64。不可思议的是cpu型号是intel(R) Core(TM) i3-2120 cpu @ 3.30GHZ,不知道怎么解释。但是感觉这种方法也不是很靠谱。
试了以上四种方式觉得不靠谱之后就没再查其它方法了。
5) 据说IsWow64可以,没有测试
3. 64位windows系统上写注册表失败
代码如下:
::RegOpenKeyExA(HKEY_CLASSES_ROOT, regSubKeyName, 0, KEY_ALL_ACCESS, &hKey);
::RegSetValueExA(hKey, "", 0, REG_SZ, (LPBYTE)"test", strlen("test")+1);
打开注册表项是成功的,而写注册表函数总是返回5,即ERROR_ACCESS_DENIED,首先排除了两个原因,因为用的帐号是管理员,而且打开函数中设置KEY_ALL_ACCESS,包括了写权限。手动打开注册表并修改,是没有任何问题的。隐约觉得是UAC搞得鬼,将其设位最低和最高,都没有起作用。
最后的解决方法让人吐血,右键单击应用程序图标,选择“以管理员权限”运行,一切ok了。
真是杯具,我为我的哨子付出了太大的代价。