下半年公司有一个Windows下64Bit移植的项目,已经厌倦之前百无聊赖的维护工作的我,有幸接到了这项工作。废话不多说,接下来谈谈Windows下64Bit移植的心得。
项目特点: Visual Studio 2005 (包括MFC应用程序以及Win32 DLL) + WDK7000 (驱动程序) 环境
心得1: 调整目录结构的原因是同时支持64Bit和32Bit的编译,这时候可能对应的要修改很多脚本来应对这些目录的变化。
心得1: 对于VC项目,记得在追加了x64编译选项的时候,追加_WIN64的宏定义。 按照MSDN的说法,_WIN64和_AMD64_对于64Bit编译环境应该是默认存在的,而且_WIN32宏并不需要去掉。 (因为像RC文件也许要这样的宏,直接在Visual Studio中一个一个添加实在会把人累死,建议可以通过脚本修改)
心得2: 对于使用WDK环境,如果支持从XP到Win7的多个操作系统,一定要注意NTDDI_VERSION, _NT_TARGET_VERSION, _WIN32_WINNT的指定(否则可能编出来的DLL压根不能用)。 32Bit项目支持一些较早的系统(如Win2000),显然64Bit项目应该是与时俱进的。
心得3: 记得WinSXS与DLL Hell的事情,一定要检查manifest文件,或者linker声明中对于系统特定版本DLL的依赖关系。32Bit项目下可能支持了特定版本的DLL(比如6.0的comctl32.dll)。 64Bit也一定要支持。
心得4: 如果整个项目(VS+WDK)由统一的脚本控制编译,一定要注意编译器,链接器的路径冲突问题(防止用VS2005的编译工具去编WDK的代码)。 还有就是一些lib文件引用的冲突问题。控制好各种PATH环境变量很重要。
心得5: 不要相信VS的版本移植所执行的conversion。如果从VS2003升级到VS2005, 记得检查一下配置中有哪些配置被弄丢了。比如对manifest文件的依赖
其实这里本应是64Bit移植的重头戏,如果之前的代码写的好的话, 这里基本不用什么大的调整了。可是大部分年代久远的系统,多半在产生之初都没有考虑过64Bit系统,可能那个时候amd64还没有, ia64又太激进。 代码中存在着各种稀奇古怪的bomb,一定要扫干净。
心得6: 类型,类型, 还是类型
数据类型的扩展可谓是64Bit系统与32Bit系统最大的不同,数据寻址范围增大到64位长度,指针类型扩展到64位长度,数据结构变成8字节对齐,位操作的变量很可能需要应对高32位的扩展。因此,类型,特别是各种数据类型的长度应该是bomb最密集的区域。
应对这些问题,有以下几种实践中好用的方法:
1. 重视编译过程中产生的warning。现在的编译器都很强大,对于类型及长度发生的变化,它是最直接的反应。
2. 除了编译器,可以使用静态代码分析工具。
3. 一定要检查每一处强制类型转换,"每一处"听起来有点可怕。可以通过正则表达式将这些强制类型转换都找出来。
4. 在Windows系统中,特别要注意DWORD类型,WPARAM, LPARAM, LRESULT, size_t等, 以及各种PTR结尾的类型。 因为32Bit系
统中,他们很可能都用来保存指针或者范围。
心得7: 与长度有关的函数
一定要小心这些与长度相关的函数,文件长度,数组长度,内存范围, 这些东西从2^32次方的访问范围,增大到了2^64次方的范围,一个不小心,溢出就等着你。
心得8: 如果能回到过去
如果回到32Bit项目编码的一开始,你必须要记住以下几件事情,否则噩梦会伴随着你的:
1. 函数声明,定义一定要匹配。 特别是C代码,编译器有时候对是检查不错来这种事情的。
2. 尽量不要用强制类型转换,也不要图方便用一些被typedef或者#define出来的同类型不同名的类型声明。
不要随便使用你认为的类型相同类型名,应该好好阅读系统建议你应该使用的类型名。也就是针对特定的场合,使用特定的类型名。
表示长度的时候,尽量使用size_t
表示指针操作的时候,尽量使用ptrdiff_t
LPARAM, WPARAM 不要用DWORD取代
LRESULT以及各种PTR结尾的类型不要用int或者unsigned int取代
指针不要存在DWORD变量中
。。。。。。
3. 要注意数据接口,特别是保存或者度入的数据。
因为程序导出和读入的数据有可能需要在32Bit和64Bit系统上共享。因此,到出的数据中,尽量不要使用长度发生变化的类型。对于数
据存取,最好限定在BYTE, WORD, DWORD, QWORD这些固定的数据类型上。
移植有时候会有一些不那么显而易见得问题产生,特别是Windows系统之间也有许多差异。步骤3中的动作,基本上是移植中的已知问题,但是还有许多未知的潜在不兼容性存在。而这些,是在设计之出很难考虑到的。所以兼容性测试是必须要做的,而且是大规模的回归测试。
尽管如此大规模的全面测试,这里我已经有一些应该牢记的兼容性问题,
心得9: 子进程与权限提升
WinExec, CreateProcess函数与ShellExecute函数都是用来创建子进程的函数。根据微软官方的说法,WinExec, CreateProcess函数并不支持子进程的权限提升。在Vista以后的系统中,用这两个底层函数创建安装程序进程将导致无法安装(没有权限)。 而ShellExecute被是较为高层的用户函数,它是支持权限提升的。 虽然微软这么说,但实际上这只发生在64Bit系统上。(可能默认情况下32Bit已经被修整了) 。 因为实际上32Bit下WinExec, CreateProcess仍然可以提升权限,这可能导致这个问题被忽视。
心得10: ToolTipInfo结构体大小与comctl32.dll版本
奇怪的问题,ToolTipInfo结构体大小必须剪掉一个指针的长度,才能在多数64Bit系统中正常运作。
心得11: MFC中的OnCancel事件与OnQueryCancel事件
两者的用法有区别,但32Bit下有人把它们随意的使用。结果,64Bit下会发生你把电脑砸了也想不到的现象发生。
以上就是这半年来在64Bit移植上的心得。暂时想到这么多,还比较凌乱,以后慢慢整理吧。