应用软件开发的64 位WINDOWS 系统环境兼容性

1. 64 位CPU 硬件

目前的64位CPU分为两类:x64和IA64。x64的全称是x86-64,从名字上也可以看出来它和 
x86是兼容的,原先的32位程序可以在x64上运行得很好,这也就是为什么在x64的CPU上 
能够成功安装32位的Windows操作系统。现在市场上卖的家用电脑的CPU通常都是x64。 
两家顶级CPU公司Intel和AMD分别推出了EM64T(Extended Memory 64 Technology)和 
AMD64(Advanced Micro Devices 64)。这两款x64 CPU的指令集有很多相似之处,Intel追加 
的大多数64位指令也与AMD64指令集相兼容,因此Microsoft就不用为两家公司的64位处 
理器开发各自的64位操作系统。比如Vista x64和SQL2005 x64即可以在EM64T上运行,也 
可以在AMD64上运行。 
IA64是真正的64位CPU,指令集较x86有很大变化,它不兼容32位的程序。微软的很多产 
品都不支持IA64 ,特别是面向个人用户的一系列产品。但是面向服务器的一些产品还 
是有IA64版本的,比如Windows 2003,SQL server 2005等等。由于IA64没有x64这么普及, 
所以本文中讨论的64位CPU如果不加特别说明,指的是x64。 
CPU从32位发展到64位的最大的驱动力就是为了让内存突破4GB的限制。目前的操作系 
统和应用软件都越做越大,4GB的限制成了软件发展的瓶颈。然而64位的到来一下子解 
决了这个问题,让软件发展的前途一片光明。64位CPU使用64位来表示地址空间,也就 
是2的64次方,是一个天文数字:16EB !这个范围就目前来看太大了,当前任何软件都 
用不了这么多空间,所以AMD64 CPU目前的实现只用了64位中的48位来表示地址空间。 
它们分为两块,一块是从00000000 00000000到00007FFF FFFFFFFF,还有一块是从 
FFFF8000 00000000到FFFFFFFF FFFFFFFF,一共是256TB(如下图的左边),在Windows 
下,低位的128T是给应用程序使用(User Mode),高位128TB是给系统使用(Kernel Mode)。 
如果将来有需要的话,可以扩展成56位(如上图的中间),最终还可以使用全部的64 
位(Full 64bit implementation),这样的话,虚拟内存空间将是2的64次方,共16 EB。将来 
可以把大文件整个的映射到内存里,通常来说,这样会使文件的读写速度会比现在快很 
多。

上面所说的是虚拟内存,在物理内存方面,AMD64目前的实现支持40位的物理内存,共 
1 TB,这个系统架构以后可以扩展成52位,共4PB。

内存地址空间变成了64位,那也就是意味着寄存器也是64位。先来看看最具有代表性的 
RAX寄存器。从16位时代的AX,到32位时代被扩展成EAX,现在扩展成64位的RAX,它们 
之间的关系如图

RAX, RBX, RCX, RDX, RBP, RSI, RDI, RSP和RIP都从原来的32位(EAX, EBX, ECX等等)变成了64 
位,并且还新增加了许多新的64位寄存器和128位寄存器。

2. 64位Windows系统的32位代码兼容性设计 
WOW系统 
能安装64位操作系统的机器,其CPU必然是64位的。根据IA64和X64架构的区别, 
MS分别推出了支持IA64以及X64的WINDOWS操作系统,包括XP,VISTA,2003SERVER, 
2008SERVER等。之前的32位程序要在64位系统上运行,除了CPU要兼容32位代码 
(IA64采用软件模拟方式兼容IA32和X86指令,效率不高),在操作系统层面必 
然也要有一定的措施进行兼容。由于IA64的系统并不常见,因此我们的讨论针对 
X64架构的硬件和软件系统。 
64位Windows为了兼容32位应用程序,微软采用了WOW64技术。WOW的全称是Windows 
On Windows,前一个Windows指32位,后一个是64位,意思就是说在64位Windows上的 
32位Windows。WOW技术的目的是兼容以前的32位应用程序,使得它们在64位 Windows 
上运行结果和在32位Windows上运行结果一样。 
在64位环境下,System32目录下存放的是64位DLL。为了能兼容32位应用程序,64位 
Windows也提供了一整套32位的系统DLL。他们存放在Syswow64目录下。从名字上看有 
点混淆:System32是放64位DLL的,Syswow64是放32位DLL的。在Windows x64环境中, 
用32位的Task Explorer查看32位应用程序的进程,可以发现,相关联接的系统DLL基本上 
都是在Syswow64目录下的。 
如果使用64位的Task Explorer查看32位的程序,会发现多出了3个特殊的DLL:wow64,

wow64win和wow64cpu和一个64位的ntdll.dll。

其中64位的ntdll.dll是64位系统下WINDOWS进程都加载的系统DLL,主要提供相关的系 
统调用的接口。另外三个DLL就是用来创建64位系统下的模拟32位系统环境。Wow64.dll 
管理进程和线程的创建,截获异常分发处理(Exception Dispatching)和系统调用 
(ntolkrnl.exe所暴露出来的系统调用),这个DLL还实现了文件系统重定向(File System 
Redirection),注册表重定向(Registry Redirection),和注册表反射(Registry Reflection)。这 
三种技术会在后面详细介绍。 Wow64cpu.dll管理wow里每个正在运行的线程的32位CPU 
上下文,并且控制CPU在 32位模式和64位模式之间的切换。 Wow64win.dll截获GUI系统 
调用,这些调用是Win32k.sys暴露出来的。 由此可以看出,在64位系统下得32位程序, 
用到的系统内核是64位而不是32位的。

上面所说的三个DLL共同实现了在64位Windows上WOW技术。如下图。

在32位系统中,默认情况下,应用程序可以使用2G内存,另外2G是系统使用的;通过 
修改配置,应用程序能使用3G内存,这时系统使用1G内存。在WOW64环境下,一个32 
位应用程序能使用最多全部的32位地址空间:4G !只需要在编译程序的时候,设置 
IMAGE_FILE_LARGE_ADDRESS_AWARE标志(/LARGEADDRESSAWARE开关),如果不打开这 
个开关,就和原来一样,只能使用2G内存。

文件系统重定向器(File System Redirector) 
在32位Windows上的程序访问system32目录里的DLL,这些DLL是32位的。在64位windows 
中System32目录里是64位的DLL,所以32位程序在64位环境下需要另外一个目录作为它 
system32目录,这个目录就是syswow64,WOW64提供了文件系统重定向器(File System 
Redirector)。在WOW64环境下,32位程序访问system32目录里的文件,都会被重定向到 
syswow64目录。比如在32位程序中想访问%windir%\system32\user32.dll,那它实际上访 
问的是%windir%\syswow64\user32.dll。 
重定向也有一些例外,下面这些目录是不需要被重定向的: 
%windir%\system32\catroot 
%windir%\system32\catroot2 
%windir%\system32\drivers\etc 
%windir%\system32\logfiles 
%windir%\system32\spool 
使用如下的一个小程序。先输出system32下的notepad.exe文件大小,再输出 
system32\drivers\etc\hosts的文件大小。然后调用WOW API: 
Wow64DisableWow64FsRedirection把文件系统重定向器禁止掉,然后再次输出两个文件 
的大小。

void PrintFileSize(PCWSTR wszFileName) 

wprintf(L"%ls ", wszFileName); 
HANDLE hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, 
NULL, OPEN_EXISTING, 0, NULL); 
// Check hFile value. If opening file succeeded, print file size 
wprintf(L"File Size: %d Bytes\r\n", GetFileSize(hFile, NULL)); 
CloseHandle(hFile); 

void wmain(int, WCHAR*) 

PrintFileSize(L"c:\\windows\\system32\\notepad.exe"); 
PrintFileSize(L"c:\\windows\\system32\\drivers\\etc\\hosts"); 
PVOID pOldValue; 
Wow64DisableWow64FsRedirection(&pOldValue); // disable redirection 
PrintFileSize(L"c:\\windows\\system32\\notepad.exe"); 
PrintFileSize(L"c:\\windows\\system32\\drivers\\etc\\hosts"); 
Wow64RevertWow64FsRedirection(pOldValue); // restore redirection 

程序编译成32位后,在64位Windows上运行的结果是: 
可以看到在没有禁用文件系统重定向器之前,notepad.exe的大小是151040字节,禁用后 
是169472字节,打开“我的电脑”到Syswow64目录下找到notepad.exe,它的大小是 
151040,而system32下则是169472,虽然在程序中写的都是system32下的notepad.exe, 
实际上访问的却是两个不同的文件。再看一下hosts文件,它在禁用前后的大小是一模一 
样的761字节,说明它没有被重定向过,可以打开%windir%\system32\drivers\etc目录看 
一下,hosts文件就是761字节。

注册表重定向器(Registry Redirector) 
和目录重定向类似,注册表的一些键也需要重定向,它们是: 
HKEY_LOCAL_MACHINE\Software 
HKEY_USERS\*\Software\Classes 
HKEY_USERS\*_Classes 
上面的星号(*)表示用户的SID(Security ID)。也就是说32位程序访问 
HKEY_LOCAL_MACHINE\Software\XXX和64位程序访问 
HKEY_LOCAL_MACHINE\Software\XXX是完全独立,不相关的。比如在32位程序在 
HKEY_LOCAL_MACHINE\Software\下新建了一个值ABC,64位程序在访问 
HKEY_LOCAL_MACHINE\Software\的时候会看不到这个ABC值。 
再来做一个试验:64位Windows提供了两个版本的注册表编辑器(regedit.exe),一个版本 
是32位的:%windir%\SysWOW64\regedit.exe,另一个是64位的:%windir%\regedit.exe。 
现在,先打开64位的注册表编辑器,在HKEY_LOCAL_MACHINE\Software\下面新建一个键 
aaa。 
然后关掉64位的注册表编辑器(注意:必须关掉,不然32位的注册表编辑器无法打开), 
打开32位的版本。发现HKEY_LOCAL_MACHINE\Software\和64位的时候很不一样,而且看 
不到aaa键。现在,再新建一个aaa2。

关掉32位版本,再打开原先的64位版本,可以发现HKEY_LOCAL_MACHINE\Software\只有 
aaa键,没有aaa2键。打开wow6432Node,会惊奇的发现原来aaa2在这里。实际上当32 
位程序访问或修改注册表的键时,注册表重定向器把它们重定向到了wow6432Node键。

如果一个32位程序就想访问64位的注册表,或者64位程序想访问32位的注册表,在调用 
注册表API:RegCreateKeyEx,RegDeleteKeyEx和RegOpenKeyEx的时候可以加上标志 
KEY_WOW64_64KEY或者KEY_WOW64_32KEY。前者表明不管程序是32位或者64位,一律 
访问64位的注册表,后者表明一律访问32位注册表。 
和文件系统重定向相似,注册表重定向也有一些例外: 
HKEY_LOCAL_MACHINE\Software\Classes 
HKEY_LOCAL_MACHINE\Software\Microsoft\COM3 
HKEY_LOCAL_MACHINE\Software\Microsoft\EventSystem 
HKEY_LOCAL_MACHINE\Software\Microsoft\Ole 
HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc 
HKEY_USERS\*\Software\Classes 
HKEY_USERS\*_Classes 
WOW64使用反射技术(Reflection)来处理这些例外情况。比如在32位程序在 
HKEY_LOCAL_MACHINE\Software\Classes下新建了一个值 XYZ,这个值会立刻复制到64 
位的HKEY_LOCAL_MACHINE\Software\Classes下面,所以当64位程序在访问 
HKEY_LOCAL_MACHINE\Software\Classes的时候就能访问到一样的XYZ值。Windows还提 
供了2个API来打开和禁用反射功能:RegEnableReflectionKey和RegDisableReflectionKey。

对于COM使用的注册表HKEY_LOCAL_MACHINE\Software\Classes\CLSID,它十分特别,是 
特例中的特例。在处理这个键时,注册表重定向器会做一个相对复杂点的判断:如果注 
册的32位COM是local server(客户端和COM是两个独立的进程[5]),那注册的信息会被 
反射到64位,如果是in-proc(COM是一个DLL,客户端在使用这个COM的时候会把DLL加 
载到客户端进程[5]),那不会被反射。为什么需要这种判断呢?因为一个32位的local 
server COM是运行在一个独立的进程中,它即能够被32位程序调用,也能被64位程序调 
用。但是32位in-proc COM的话是需要把这个32位DLL载入到64位进程中去,这是做不到 
的,也就是说32位in-proc COM无法为64位程序提供服务,那也就没有必要把它的注册表 
信息反射到64位了。

驱动程序和系统服务 
对于驱动程序,64位系统只支持64位的驱动程序,因此对于代码中有驱动或者系统服务 
的情况,必须重新编译成64位的可执行代码,并且由于64位系统有强制的签名验证,未 
签名的驱动只能在开启调试状态的模式下加载。

3. 64 位编程代码注意事项(C++) 
与JAVA 等依靠虚拟机解释运行的程序不同,C/C++代码通常被编译成机器码执行,虽然 
提供了更快地速度和更强的功能,但也带来很多代码兼容性的问题。

数据类型 
C++数据类型的变化,从下面的表格中可以看到:char, short, int, long, float, double 在64

位Windows 环境下的大小没有变化,但是size_t, time_t 和指针的大小都从32 变成了64 
位,8 个字节。

Type Name x86 x64 windows 
char 8 8 
short 16 16 
int 32 32 
long 32 32 
float 32 32 
double 64 64 
size_t 32 64 
在Windows 下,一些数据类型也发生了变化,这里列举了一些最常用的类型。WPARAM, 
LPARAM, LRESULT 和各种HANDLE(比如HWND, HKEY 等等)本质上都是指针,所以他们都 
变成了64 位。

typedef UINT_PTR WPARAM; 
typedef LONG_PTR LPARAM; 
typedef LONG_PTR LRESULT; 
typedef void *HANDLE; #define DECLARE_HANDLE(name) struct 
name##__ { int unused; }; typedef struct name##__ *name 
DECLARE_HANDLE(HWND); DECLARE_HANDLE(HKEY); 
我们在32位CPU下编写的正确代码,到了64位下可能会出问题,这些问题通常只能通过 
人工逻辑来逐个判断修改。

特殊数字兼容性 
如果程序里出现了下列这些数字,有可能需要再检查一遍代码,看看有没有问题。 
4:32位时代一个指针的字节数 
32:32位时代一个指针的位数 
0x7fffffff:32位有符号整数的最大值,有时用来进行位操作(与、或、异或) 
0x80000000:32位有符号整数的最小值 
0xffffffff:32位无符号整数的最大值。同时也是有符号整数-1,通常会被用来作为出错值, 
或者无效值。 
还有其他一些情况无法一一列举,但是在编成的时候利用SDK中定义的一些宏往往能避 
免这些问题。

整数保存在双精度浮点数中 
下面的程序, 32位中a等于c,但在64位中a就不等于c了 
size_t a = -1; 
double b = a; 
a--; 
b--; 
size_t c = b; // x86: a == c 
// x64: a != c 
原因就是双精度类型 double 一共有 64 位(不管是 32 位环境还是 64 位环境),根据

IEEE-754, double 用1 位表示数字的符号,用 11 位表示指数,52 位表示尾数。尾数 
也就是所能表示的数字的精度。尾数的位数越多,所能表示的数字的精度越高。32 位(x86) 
时代的size_t 是32 位,有52 位尾数的double 类型能绰绰有余的精确表示size_t。但是 
到了64 位时代,size_t 是64 位,这时double 的52 位尾数没有办法精确表示size_t,所 
以导致了a 不等于c。所以在64 位环境中,尽量不要将整数保存在双精度浮点数中。

特别小心位移操作 
看下面这段代码,想想i分别会是什么值。(__int64是64位有符号整数) 
__int64 i; 
i = 1 << 31; // i = ? 
i = 1 << 32; // i = ? 
您可能会认为一个是0x00000000 80000000,另一个是0x00000001 00000000。实际上在 
第二行运行后i是0xffffffff80000000;第三行运行后i是0x0000000000000000,为什么呢? 
编译器在处理1<<31的时候是按照32位整型来处理,得到的结果是0x80000000,由于i是 
64位有符号整数,在赋值操作的时候,把0x80000000,就转成了0xffffffff 80000000。同 
样,第三行的1<<32也是按照32位整型来处理的时候,得到的是0,所以把32位整数0转 
成64位整数后还是0。正确的写法应该如下,先把1转成64位,这样在位移操作的时候已 
经是64位了。 
__int64 i; 
i = __int64(1) << 31; 
i = __int64(1) << 32; 
5.不要混合使用32 位类型和64 位类型 
对于此要点,大家需要特别注意,一般的测试比较难发现这个问题,因为它在数字较小 
的情况下,一切正常,一旦数字变大,则会出现严重的错误。先来看下面一段代码,想 
想有什么不正确的地方。 
size_t Count = SomeValue; 
for (unsigned Index = 0; Index < Count; ++Index) 
{ ... } 
前面说过size_t在64位环境下是64位,unsigned在64位环境下还是32位,如果Count是一 
个很大的数字,超过了32位无符号整数的最大值,那条件Index <count永远成立,导致了
死循环。正确的写法应该是把Index也申明成size_t类型,和Count一致。 
再看下面这个例子。 
int x = 100000; 
int y = 100000; 
int z = 100000; 
__int64 r1 = x * y * z; // -1530494976 
__int64 r2 = __int64(x) * y * z; // 1000000000000000 
__int64 r3 = x * y * __int64(z); // 141006540800000

如果.NET程序使用了32位的in-proc COM,在编译的时候需要指定它的目标平台为x86 
.NET 1.0和.NET 1.1没有64位版本,但是从.NET 2.0开始提供了x64版本。C#,VB.NET等.NET 
语言的程序在编译的时候默认的目标平台(Platform target)是Any CPU,意思是如果运行 
环境是32位的,那么按照32位应用程序运行,如果在64位Windows上运行,则按照64位 
应用程序运行。 
回想一下“注册表重定向器”里提到的:64 位应用程序没有办法得到32 位in-proc COM的 
注册信息。也就是没有办法使用这个 COM。所以如果.NET 程序使用到了 32 位 in-proc 
COM,那就不要使用默认的“Any CPU”选项,我们应该指定它为x86。要不然您的程序在 
x64 的Windows 环境下,将无法调用这个COM。下图是Visual Studio Orcas 的C#工程属 
性。

"Detect 64-bit Portability Issues"编译开关 
VisualStudio 里面的“Detect 64-bit Portability Issues”编译开关。这样,编译器可以帮您发 
现大多数的 64 位问题。虽然打开这个开关会稍微增加一些编译时间,但是绝对值得这 
么去做。

代码检查(Code Review) 
这是最重要的一点,前面所说的这些要点能覆盖大多数的问题,但不是全部。所以在写 
完代码后,一定要做检查,最好找别人帮您检查。像微软和其他知名的软件公司都十分 
重视代码检查,不经别人检查的代码是不允许提交的。

让我们用一个例子来结束这篇文章,请看下面这段32位时代的代码 
PVOID p; 
.......... 
int n = int(p); 
.......... 
p = PVOID(n); 
如果把这段代码移植到64位,很多人会写成这样: 
PVOID p; 
.......... 
__int64 n = __int64(p); 
.......... 
p = PVOID(n);

http://www.freedocuments.info/7522137270/

64位WINDOWS系统环境下应用软件开发的兼容性问题(CPU 注册表 目录)的更多相关文章

  1. 64位Windows系统下32位应用程序连接MySql

    1.首先得安装“Connector/ODBC”,就是Mysql的ODBC驱动,这个是与应用程序相关的,而不是与操作系统相关的,也就是说,不管你的系统是x64还是x86,只要你的应用程序是x86的那么, ...

  2. 64位windows 7下成功配置TortoiseGit使用Github服务器

    最近感觉自己电脑上的代码太乱了,东一块.西一块……于是决定使用正规的源代码管理软件来管理自己以后写的代码.以前做小项目的时候用过TortoiseSVN,感觉不错,但是速度上有点慢,于是决定尝试一下新东 ...

  3. 64位windows 7下配置TortoiseGit(转)

    原文:http://our2848884.blog.163.com/blog/static/146854834201152325233854/ 最近感觉自己电脑上的代码太乱了,东一块.西一块……于是决 ...

  4. 招商银行支付dll在64位windows系统下的注册使用问题

    按照文档中的说明,注册完dll后,依然报找不到COM组件的错误.尝试过以下方法: 1.在VS中将项目编译目标改为x86,只能解决VS可以启动程序的问题,一部署到IIS中就出错. 2.估计是因为权限问题 ...

  5. 浅谈Excel开发:十一 针对64位Excel的插件的开发和部署

    自Office 2010版本开始有了32位和64位之分,对Excel来说,32位的Excel和64位的Excel在性能上的主要区别是64位的Excel能够处理2G及2G以上的大数据集. 随着64位操作 ...

  6. [百度空间] [转]将程序移植到64位Windows

    from : http://goooder.bokee.com/2000373.html (雷立辉 整理) 简介:本文对如何将32位Windows程序平滑的支持和过渡到64位Windows操作系统做出 ...

  7. 64 位 Windows 平台开发注意要点之文件系统重定向

    Program Files 的重定向 很多开发人员都知道,在 64 位 Windows 系统上,32 位程序是无法获取得到 C:\Program Files 的完整路径的,只能获取到 C:\Progr ...

  8. Winio驱动在64位windows下无法使用的解决方法

    C#在使用WinIo的驱动开发类似按键精灵一类工具的时候,需要对相关的驱动进行注册才能正常启动,找了下资料,资料来自: http://jingyan.baidu.com/article/642c9d3 ...

  9. [转] 64位windows下添加postgreSQL odbc数据源

    系统环境:windows7 64位 postgreSQL9.0(64bit)   ps:安装postgreSQL时确定安装了odbc驱动.   问题:点击“开始->控制面板->管理工具-& ...

随机推荐

  1. Spring-----Spring整合Struts2实例

    转载自:http://blog.csdn.net/hekewangzi/article/details/51713058

  2. 利用oxygen编辑并生成xml文件,并使用JAVA的JAXB技术完成xml的解析

    首先下载oxygen软件(Oxygen XML Editor),目前使用的是试用版(可以安装好软件以后get trial licence,获得免费使用30天的权限,当然这里鼓励大家用正版软件!!!) ...

  3. 关于wireshark的两个抓包过滤显示的基本语法

    关于wireshark的两个基本语法 关于wireshark的两个基本语法 1. Capture Filters 语法:<Protocol name><Direction>&l ...

  4. 精读《javascript高级程序设计》笔记二——变量、作用域、内存以及引用类型

    变量.作用域和内存问题 执行环境共有两种类型——全局和局部 作用域链会加长,有两种情况:try-catch语句的catch块,with语句. javascript没有块级作用域,即在if,for循环中 ...

  5. threadid=1: thread exiting with uncaught exception (group=0x40db8930)

    异常信息如下: 07-26 17:23:49.521: W/dalvikvm(29229): threadid=1: thread exiting with uncaught exception (g ...

  6. 你的阅读造就了你 You are what you read

    在豆瓣上看到的一篇很有思想和正能量的文章,在这里请允许我用原创的方式来呈现给大家.如果你是在校的大学生或者研究生博士生,这篇文章会让你有很多的共鸣.如果你已真正的踏入这个社会,也将受益匪浅.   电脑 ...

  7. python初学笔记(二)

    注释: 任何时候,我们都可以给程序加上注释.注释是用来说明代码的,给自己或别人看,而程序运行的时候,Python解释器会直接忽略掉注释,所以,有没有注释不影响程序的执行结果,但是影响到别人能不能看懂你 ...

  8. Probability theory

    1.Probability mass functions (pmf) and Probability density functions (pdf) pmf 和 pdf 类似,但不同之处在于所适用的分 ...

  9. COB (Chip On Board) 製程介紹/簡介/注意事項 II

    銀膠 (Silver glue) 如果晶圓有接地或是散熱需求時,一般都會採用[銀膠],如果沒有的話則會採用[厭氧膠].[厭氧膠]顧名思義就是阻隔它與空氣接觸後就會自然固化,不需要高溫烘烤.使用銀膠則需 ...

  10. [Drools]JAVA规则引擎 -- Drools

    Drools是一个基于Java的规则引擎,开源的,可以将复杂多变的规则从硬编码中解放出来,以规则脚本的形式存放在文件中,使得规则的变更不需要修正代码重启机器就可以立即在线上环境生效. 本文所使用的de ...