Windows 64 位版本的 C/C++ 编程 ghost64 windows版本
1.64位编码指导原则
Windows 64 位版本使用 LLP64 数据模型。这意味着标准 C 类型 int 和 long 保持为 32 位整数。数据类型 size_t 将映射到处理器词大小(IA32 为 32 位,IA64 为 64 位),并且 __int64 是 64 位整数。在协助迁移 32 位代码时就会完成上述操作。意义在于您可以对应用程序的 32 位版本和 64 版本使用相同的代码库。
还有一个称为 LP64 的数据模型,它将标准的 C 类型 long 映射到 64 位整数,并使 int 保持为 32 位的整数。这种数据模型常见于 Unix 平台,但从单个代码库同时创建应用程序的 32 位和 64 位版本时可能有一些困难。您可能注意到了此处的常见主题。32 位平台与 64 位平台的思想就是应该能够从单个代码库中构建两个版本的应用程序。如果无法做到,那么您可能要重新审视您的设计。具有单个代码库就是巨大的胜利,尤其是如果您计划发行两个版本。
综合以上考虑,建议如下:
l 使用Windows64位或32位安全数据类型;
l 检查所有的指针运算及使用指针的地方;
改写所有嵌入的汇编代码;使用intrinsics或者native assembly code;
l 对于X64相关代码使用 #if defined (_AMD64__) 的预编译命令(没有定义__IA64__宏);
l 对于IA64相关的代码使用#if defined (__IA64__)命令;
l 编译x64程序使用AMD64处理器;
用 Visual C++ 创建在 64 位 Windows 操作系统中运行的应用程序时,应注意以下问题:
l 在 64 位 Windows 操作系统中,int 和 long 是 32 位值。
l 在 64 位 Windows 操作系统中,size_t、time_t 和 ptrdiff_t 是 64 位值。
l 在 32 位 Windows 操作系统中,time_t 是 32 位值。
l 应注意代码在哪里采用 int 值和将其作为 size_t 值或 time_t 值处理。数字有可能增长得比 32 位数大,并且数据在被传递回 int 存储时有可能被截断。
l %x(十六进制 int 格式)printf 修饰符在 64 位 Windows 操作系统中不按预期的那样工作;它只对传递给它的值的前 32 位进行操作。
l Windows 32 位操作系统使用 %I32x 显示整数。
Windows 64 位操作系统使用 %I64x 显示整数。
l %p(指针的十六进制格式)在 64 位 Windows 操作系统中按预期的那样工作。
2./WP64:使编译器警告您潜在的问题
Microsoft_ Visual C 和 Microsoft_ Visual C++_ .NET 2002 编译器添加了 /WP64 开关,这使您可以测试 32 位代码的 64 位兼容性问题。编译器将发出有关指针截断和不正确转换的警告。将 32 位应用程序迁移到 Windows 64 位版本中前面的一个步骤就是打开这个标记,然后就像通常编译代码那样来编译您的代码。第一次会有几个错误。例如,请看下面这个代码片段:
DWORD i = 0;
size_t x = 100;
i = x; // C4267: warning C4267: '=' : conversion from
// 'size_t' to 'DWORD', possible loss of data.
在 32 位的平台上,这段代码能够很好的进行编译,因为 size_t 是 32 位的,但是在 64 位的平台上,size_t 就是 64 位的整数。启用 /WP64 后,编译器将会警告您类似的情况。
其他示例:
void func(DWORD context)
{
char* sz = (char*)context; // C4312: warning C4312:
// 'type cast' : conversion
// from 'DWORD' to 'char *' of
// greater size
// Do something with sz..
}
char* string = "the quick brown fox jumped over the lazy dog.";
func((DWORD)string); // C4311: warning C4311: 'type cast' :
// pointer truncation from 'char *'
// to 'DWORD'
在修复这些错误后,请测试您的 32 位代码。您希望确保 32 位的代码继续按预期那样工作。32 位和 64 位二进制文件应该从相同的代码库中构建。这就是编写不断前进的 Windows 应用程序的关键概念。开始时,您需要考虑 32 位和 64 位的问题,并且为应用程序编写可以运行在这两个平台上的代码。
3.多态类型
由于 Win32 API 是针对 C 的,在很多情况下,您都需要将整数转换成指针或者相反。在 32 位的硬件上不会有问题,其中指针的大小和整数的大小是相同的,但在 64 位的硬件上却完全不一样。这就是多态类型出现的原因。
对于特定的精度,您可以使用固定精度的数据类型。不管处理器的词大小如何,它们的大小都是一致的。大多数这些类型都在它们的名称中包含精度,可以从下面的表中看出:
表 1. 固定精度的数据类型
类型
定义
DWORD32
32 位无符号整数
DWORD64
64 位无符号整数
INT32
32 位有符号整数
INT64
64 位有符号整数
LONG32
32 位有符号整数
LONG64
64 位有符号整数
UINT32
无符号 INT32
UINT64
无符号 INT64
ULONG32
无符号 LONG32
ULONG64
无符号 LONG64
此外,当您需要数据类型的精度随着处理器词大小变化时,请使用指针精度数据类型。这些类型又称为“多态”数据类型。这些类型通常以 _PTR 后缀结尾,如下面的表格所示:
表 2. 指针精度的数据类型
类型
定义
DWORD_PTR
指针精度的无符号长类型
HALF_PTR
指针大小的一半。用于包含一个指针和两个小型字段的结构中
INT_PTR
指针精度的有符号整型
LONG_PTR
指针精度的有符号长类型
SIZE_T
指针可以引用的最大字节数。用于必须跨指针的整个范围的计数
SSIZE_T
有符号 SIZE_T
UHALF_PTR
无符号 HALF_PTR
UINT_PTR
无符号 INT_PTR
ULONG_PTR
无符号 LONG_PTR
LPARAM
与 LONG_PTR 为同义词,(在WTypes.h 中定义)
WPARAM
与 UINT_PTR 为同义词,(在 WTypes.h 中定义)
通过整数参数传递参数或上下文信息的所有 Win32 API 都更改为使用这些新的类型。SetWindowLong 和 SetWindowLongPtr 函数都是很好的示例:
旧方法:
LONG SetWindowLong(
HWND hWnd,
int nIndex,
LONG dwNewLong);
新的多态方法:
LONG_PTR SetWindowLongPtr(
HWND hWnd,
int nIndex,
LONG_PTR dwNewLong);
请注意,该函数的 xxxPtr 版本使用新的多态类型。对于开发人员而言,通过在窗口的额外数据区域中存储指针来存储窗口的上下文信息是相当常见的。使用 SetWindowLong 函数在 Windows 32 位版本上存储指针的任何代码必须更改为调用 SetWindowLongPtr。该更改非常简单并且很快就可以完成,因为大多数更改要求使用多态类型。
另外,WindowProc 和 GetQueuedCompletionStatus 也是很好的示例:
LRESULT CALLBACK WindowProc(
HWND hWnd,
UINT uiMsg,
WPARAM wParam,
LPARAM lParam);
BOOL GetQueuedCompletionStatus(
HANDLE hCompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds);
WindowProc 使用 LPARAM,后者是多态类型。GetQueuedCompletionStatus 使用 ULONG_PTR,后者也是多态类型。这使那些假设整数的大小与指针大小相同的现有代码可以在进行很少修改的情况下继续工作。
Microsoft_ Visual Studio_ .NET 2002 的编译器包含两个新的优化模式:Link Time Code Generation(LTCG,又称 Whole Program Optimization)和 Profile Guided Optimization (PoGO)。代码优化在 Itanium 处理器上比在 x86 平台上更为重要,因为编译器对生产高效代码负有全部责任。对于编译器方面的知识,请参考MSDN中文网站http://www.microsoft.com/china/MSDN/library/windev/Windows2003/NFdnnetservws0364bitdev.mspx来获得更多此方面的内容。
七.将驱动移植到64位Windows操作系统
x64位操作系统和x32位操作系统的最大区别就是内存寻址方式的不同。而64位操作系统不支持32位的驱动程序,因为驱动程序和windows内核同处于一个地址空间中。这是移植32位驱动到64位驱动的最大原因。当然,64位驱动程序可以使用更大的分页内存,非分页内存及系统缓存。而且,你的设备从此就支持64位windows操作系统了。
1.在X64下的驱动程序安装
除了要把应用程序的32位驱动程序变成64位程序之外,驱动的安装程序和其它配置文件同样需要修改。也就是说,对于要在x64上运行的32位程序,它所依赖的驱动仍然需要是64位的。这些相关程序包括inf文件,device installers, class installers和co-installers。相关资料可查看MSDN Libarary DDK:Porting Your Driver to 64-Bit Windows。
所以,要改造应用程序的安装程序。方法是,让32位版的驱动安装为缺省安装选项,即用户插入安装光盘之后,依然运行32位安装程序。但当程序调用UpdateDriverForPlugAndPlayDevices返回值为ERROR_IN_WOW64时,这说明该安装程序正运行在64位Windows环境中。此时,这个安装程序应该调用CreateProcess函数来启动64位的安装进程。这个64位的安装进程通过调用64位驱动目录下的inf文件进行驱动安装。
2.驱动要支持32位IOCTL
某些IOCTL可能包含含有指针的结构,所以,要特别小心的区别对待它,必须根据被调用者解析结构或者输出结构。
有三种办法可以解决这个问题:
1. 尽量避免使用IOCTL传递包含有指针的结构;
2. 通过API IoIs32bitProcess()来判断上层调用者的程序类型;
3. 在64位程序中采用新的IOCTL命令;
例子:
IOCTL structure in header file
typedef struct _IOCTL_PARAMETERS {
PVOID Addr;
SIZE_T Length;
HANDLE Handle;
} IOCTL_PARAMETERS, *PIOCTL_PARAMETERS;
32-bit IOCTL structure
//
// This structure is defined
// inside the driver source code
//
typedef struct _IOCTL_PARAMETERS_32 {
VOID*POINTER_32 Addr;
INT32 Length;
VOID*POINTER_32 Handle;
} IOCTL_PARAMETERS_32, *PIOCTL_PARAMETERS_32;
32-Bit and 64-Bit IOCTL
#ifdef _WIN64
case IOCTL_REGISTER:
if (IoIs32bitProcess(Irp)) {
/* If this is a 32 bit process */
params32 = (PIOCTL_PARAMETERS_32)(Irp>AssociatedIrp.SystemBuffer);
if(irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(IOCTL_PARAMETERS_32)) {
status = STATUS_INVALID_PARAMETER;
} else {
LocalParam.Addr = params32->Addr;
LocalParam.Handle = params32->Handle;
LocalParam.Length = params32->Length;
/* Handle the ioctl here */
status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(IOCTL_PARAMETERS);
}
} else { /* 64bit process IOCTL */
} else { /* 64bit process IOCTL */
params = (PIOCTL_PARAMETERS)
(Irp->AssociatedIrp.SystemBuffer);
if (irpSp->Parameters.DeviceIoControl.InputBufferLength
< sizeof(IOCTL_PARAMETERS)) {
status = STATUS_INVALID_PARAMETER;
} else {
RtlCopyMemory(&LocalParam, params,
sizeof(IOCTL_PARAMETERS));
/* Handle the ioctl here */
status = STATUS_SUCCESS;
}
Irp->IoStatus.Information = sizeof(IOCTL_PARAMETERS);
}
break;
3.64-Bit INF 文件要求
在Windows Server 2003SP1之后,64位驱动的安装被提高了要求。这可以简化用户的操作及提高安全性。
Inf文件中必须含有NTAmd64或者NTIA64之类的修饰符才行。具体做法是在[Manufacturer]和Models小节都需要添加此类的字段。
[Manufacturer]
%mycompany% = MyCompanyModels
[MyCompanyModels]
%MyDev% = mydevInstall,mydevHwid
[Manufacturer]
%mycompany% = MyCompanyModels,NTx86,NTAmd64
[MyCompanyModels.NTx86]
%MyDev% = mydevInstallx86,mydevHwid
[MyCompanyModels. NTAmd64]
%MyDev% = mydevInstallAmd64,mydevHwid
如果只需要在WindowsX64系统上安装,则只需要使用NTAmd64修饰符就可以了。更多请参考http://www.microsoft.com/whdc/driver/install/64INF_reqs.mspx。
4.编程中容易碰到的问题
1)指针的相关问题
如果原有项目的编程风格控制不严,指针类型混用,强制转换使用过多等等可能对移植是一个巨大的考验。另外,程序中存在结构之中根据具体数据类型来计算其它变量的位置此类的代码也需要重新检查。
使用指针的原则如下:
1. 不要将指针强制转换为int, long, ULONG, DWORD等类型,而应该使用UINT_PTR和INT_PTR;
2. 使用PtrToUlong()和PtrToLong()来截断指针;
3. 永远不要将已经截断的存贮在int或者ULONG中指针地址的重新合成一个新的指针地址;
4. 小心的计算缓冲区的大小,说不定缓冲区的长度比ULONG所能存储的最大数都大!
5. 小心的调用那些传出指针的函数;
对4可以举个例子:比如说有两个地址ptr2(高地址), ptr1(低地址),则len = ptr2 – ptr1 将有可能大于2的32次方。
2)结构的内存排列问题
在64位的操作系统上,结构的内存排列(structure alignment)也需要小心审查。内存排列的齐整有利于处理器的执行效率。如果打开了一些编译选项,为了对齐内存地址,编译器可能会将某些位置填空。在移植过程中,对结构中的变量顺序需要仔细检查,特别是在同一个头文件中使用不同的pack选项。比如下面的代码:
#pragma pack (1) /* 也可以使用编译选项/Zp(结构成员对齐)*/
struct AlignSample {
ULONG size;
void *ptr;
};
struct AlignSample s;
void foo(void *p) {
*p = p; // 将会导致访问异常
...
}
foo((PVOID)&s.ptr);
补救办法就是使用宏UNALIGNED:
void foo(void *p) {
struct AlignSample s;
*(UNALIGNED void *)&s.ptr = p;
}
当然,更好的办法就是首先将那些64位长度的数据类型变量放在结构的前端。
3)小心使用十六进制的常量,无符号整数
小心使用十六进制的常量,无符号整数等等。比如说下面的一些断言在64位系统中是错误的:
~((UINT64)(PAGE_SIZE-1)) == (UINT64)~(PAGE_SIZE-1)
PAGE_SIZE = 0x1000UL // Unsigned Long - 32 bits
PAGE_SIZE - 1 = 0x00000fff
等式左边:
// 无符号转换
(UINT64)(PAGE_SIZE -1 ) = 0x0000000000000fff
~((UINT64)(PAGE_SIZE -1 ))= 0xfffffffffffff000
等式右边:
~(PAGE_SIZE-1) = 0xfffff000
(UINT64)(~(PAGE_SIZE-1))=0x00000000fffff000
所以:
~((UINT64)(PAGE_SIZE-1))!= (UINT64)(~(PAGE_SIZE-1))
还有:
DWORD index = 0;
CHAR *p;
If (p[index – 1] == ‘0’)
上面的代码将会在64位系统上出错!因为在32位系统上
p[index-1] == p[0xffffffff] == p[-1]
这是对的。但在64位系统上:
p[index-1] == p[0x00000000ffffffff] != p[-1]
再如:
-1 != 0xFFFFFFFF
0xFFFFFFFF != invalid handle
DWORD总是32位,所以要查找原有程序中所有用DWORD存贮指针的代码。另外,别忘了使用 %I来打印指针地址,而且大于0x80000000的也未必是内核态地址了。
5.开发64位驱动的工具
64位的开发工具和32位的差不多,除了必须的Windows DDK 2003之外,Windbg,Driver Verifier等都是拿手的好工具。最后,下载一份最新的WHQL测试包进行WHQL测试也是值得推荐的。
目前,学会使用Windbg来调试64位用户态和核心态程序的方法是尤为必要的。也可以购买支持64位的VC环境Visual Stdio 2005来进行64位程序开发。
参考资料
Windows XP Professional x64 Edition 概述http://www.microsoft.com/china/windowsxp/64bit/evaluation/overviews/overview.mspx
AMD64计算平台-带您进入计算的未来白皮书http://msdn.microsoft.com/library/default.asp?url=/library/en-us/kmarch/hh/kmarch/Other_f910e5d8-a732-4faa-a8d2-d4de021dc78d.xml.asp
64-bit DDK编程指导
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/kmarch/hh/kmarch/Other_f910e5d8-a732-4faa-a8d2-d4de021dc78d.xml.asp
硬件测试资料
http://www.microsoft.com/whdc/system/platform/64bit/default.mspx
更多阅读
32位和64位的区别 如何查看系统是32还是64位 xp系统是32位还是64位
【32位和64位的区别】如何查看系统是32还是64位——简介 最近有个朋友问我。32位和64位系统到底有什么区别的区别?WIN7系统32位和64位有什么区别?怎么查看我的系统是不是64位的?不能安装64位系统是怎么回事?怎么看系统多少位的。带着这
怎么在win7的64位系统上安装Protel DXP 2004 proteldxp2004sp2下载
怎么在win7的64位系统上安装Protel DXP 2004——简介最近开了微机原理的课又被老师推荐这了个叫做DXP的软件,可以用来设计电路和进行仿真等,话不多说,直接进入正题吧~
怎样查看计算机是32位还是64位操作系统? 精 32位装64位系统
术语“32 位”和“64 位”是指计算机的处理器(也称为“CPU”)处理信息的方式。64 位版本的 Windows 可处理大量的随机存取内存 (RAM),其效率远远高于 32 位的系统。怎样查看计算机是32位还是64位操作系统? 精——操作平台怎样查看计算
windows7电脑系统32位和64位区别 32位和64位系统区别
windows7电脑系统32位和64位区别——简介windows7电脑系统32位和64位区别 windows7电脑系统32位和64位区别——方法/步骤windows7电脑系统32位和64位区别 1、第一点 设计初衷不同:64位操作系统的设计初衷是:满足机械设计和分析、三
教你怎么让XP的C盘减肥 c盘减肥工具
Windows操作系统在C盘空间不足的情况下,我们可以通过那些具体手段来增加C盘空间。教你怎么让XP的C盘减肥——工具/原料Windows操作系统,我的电脑,系统工具。教你怎么让XP的C盘减肥——步骤/方法教你怎么让XP的C盘减肥 1、Windows操作系