拥抱64位Windows

拥抱 64 Windows
 
 
 
还记得640K内存就足够的好日子吗?那记得是什么时候,它显得捉襟见肘了吗?这是一个日新月异的时代,我们发明了各种方法,以在有限的寻址空间内,映射更多的内存:首先,是“扩充内存”(EMM),起初它只是一张硬件“卡”,以64K或128K HASH(0x80b9d4)为界,切换64K的HASH(0x80b9d4)页到DOS寻址空间内;然后,我们又看到了“扩展内存”(XMS),它使保护模式映射到更多的64K区域成为了可能。而所有这些,都只是给了程序一个权宜之计,我们真正需要的是一个更大的寻址空间,这时,32位寻址就成了大众欢迎的、暂时的“止痛片”。
 
如果640K对任何人都足够,那么2GB(Windows实际上可寻址到4GB)对大多数应用程序来说都绰绰有余,但是,仍有一些程序要求越来越大的寻址空间,这样,人们又发明了寻址窗口扩展(Address Windowing Extension AWE)和实体地址延伸(Physical Address Extension PAE)等等映射技术。今天,64位处理器和两个新版本的64位Windows来到了我们身边,与16位到32位的变化不同的是,大多数今天我们使用的程序,都没有突破2GB的寻址空间,那么新处理器有什么用呢?我们何必在意它呢。
 
诚然,在可预见的未来,我们将继续编写32位程序,但不管怎么说,在这两年里,64位CPU进入桌面电脑已成了不争的事实。AMD Athlon64和Opteron处理器随处可见,而主板生产商也推出了相应的主板,价格上与同级别32位主板相近;Intel,一开始掉在了安腾处理器(Itanium)的钱眼里,为了赶上老对手,匆匆于2004年底发布了“32位扩展架构”的至强(Xeon)处理器,技术名称为IA32E或EM64T。由于Intel的安腾处理器缺乏向后兼容,现有的二进制代码运行效率不高,一如当年Win16到Win32的转变,导致OS/2的陨落和Windows在桌面的崛起一样,AMD此时看到了也抓住了这个机会。虽然Intel的处理器可高效运行本地(native)64位代码,但从经验来说,32位程序在AMD64和EM64T架构上效率都不是很高,而Microsoft微软,由于吸取了操作系统源代码兼容性的教训,所以它从现今的Win32源代码从发,构建一个64位版本的系统应该不是件难事。
 
现在,我们都在平静等待“64位扩展架构”的Windows发布,实际上,许多的电脑厂商已用EM64T或AMD64的芯片搭配32位的Windows,只是你可能没注意到而已,也许这些芯片的最令人吸引之处,就是可以同时运行耗费大量资源的32位程序。你可能此时会想,是不是8TB内存最终对每个人来说都足够了呢?尽管在理论上,64位可达到16000PB的寻址,而实际中,应用程序的寻址空间限制在7至8TB,原因是操作系统的内核,都在地址0x8000000000附近映射到每个进程之中。
 
当然,64位的变化不只在寻址上,现今,我们已经不再追求处理器频率每年翻一番,处理器频率徘徊在3GHz至4GHz之间,所以,有着64位架构的双倍总线带宽处理器(或多核心处理器),在性能上会更好一些。
不可否认,随着越来越多的64位电脑出现,64位的Windows最终也会走入寻常百姓家,现时窘境却是:是把现有程序移植到64位还是继续开发32位程序呢?大约三年前,我们的一个客户要求把Win32平台上的Unix API层移植到基于安腾的64位Windows之上,在移植过程中,发现了一些有趣的问题;本文中,主要围绕从32位移植到64位的一些相关话题,在此与大家分享。
 
 
在Windows 之上的Windows (Windows on Windows
相对于当年运行在32位Windows之下的16位程序,WoW32(Windows on Windows 32)从表面上来看,已经表现得非常不错了,关于WoW32的主要问题不是性能,而是兼容性和稳定性。在16位Windows中,由于其运行在DOS之上,所以程序间不得不以共享内存的方式来实现多任务,随着时间推移,程序逐渐运行在一个抢占式多任务环境的系统中,而且有各自的内存空间。在64位Windows中,32位进程被当作了一个在特殊转换层中的64位进程,这个转换层被称作“Win32 on Windows 64 ”,简写为“Wow64”。
在32位Windows中,总共4GB的寻址空间被划分为操作系统2GB,程序2GB,如果在boot.ini文件中使用了/3GB的启动选项(或者/Userva=3030,但如果系统超出了进程表,将不能登录)和/LARGEADDRESSAWARE链接器标志,可把内核共享系统空间移至1GB,而给应用程序3GB的内存空间。但得到一大块连续的内存空间并不像看起来那么容易,有如下一个简单的测试程序,可显示最大的可用内存块和最高位程序地址,分别用32位和64位编译器编译,并在可执行文件中设置或取消/LARGEADDRESSAWARE位,然后在不同的平台上运行它们,结果如下图所示,具体数字会因Windows版本的不同而有所变化,HKLM/Software/Microsoft/Windows NT/CurrentVersion/Windows/AppInit_DLLs中所加载的DLL对结果也有所影响。估计其最初的设计目的是为了优化页表的大小,并以减小内核可用空间和牺牲性能为代价,调整寻址空间的大小。
 
#include
#include
#include
#pragma comment(lib, "kernel32.lib")
 
bool Walk(HANDLE hProcess, SIZE_T &free_bytes, SIZE_T &max_free, SIZE_T &reserved_bytes, void * &system_base)
{
       MEMORY_BASIC_INFORMATION mbi;
       char *addr;
       void *last_base;
       SIZE_T current_region_size = 0;
       char *top = (char *) 0 + (SIZE_T) -1;
       SYSTEM_INFO si;
       DWORD last_state = 0;
 
       GetSystemInfo(&si);
       free_bytes = 0;
       max_free = 0;
       system_base = (void *) (((char *) si.lpMaximumApplicationAddress) + 1);
       reserved_bytes = top - (char *) system_base + 1;
       addr = (char *) (last_base = si.lpMinimumApplicationAddress);
 
       while (addr < si.lpMaximumApplicationAddress)
       {
              if (0 == VirtualQueryEx(hProcess, addr, &mbi, sizeof(mbi)))
              {return false;}
#if defined(DEBUG)
char name[MAX_PATH];
name[0] = '/0';
GetModuleFileName((HMODULE) mbi.BaseAddress, name, sizeof(name));
printf("AllocationBase: %x, Base: %x Size: %x, State: %x, Dll: %s/n", mbi.AllocationBase, mbi.BaseAddress, mbi.RegionSize, mbi.State, name);
#endif
              if (last_state == mbi.State)
              {
                     current_region_size += mbi.RegionSize;
              }
              else
              {
                     if (MEM_FREE == last_state)
                     {
                            if (current_region_size > max_free)
                            {
                                   max_free = current_region_size;
                            }
                     }
                     current_region_size = mbi.RegionSize;
              }
              if (MEM_FREE == mbi.State)
              {
                     free_bytes += mbi.RegionSize;
              }
              last_base = mbi.AllocationBase;
              addr += mbi.RegionSize;
              last_state = mbi.State;
       }
       return true;
}
 
int main(int argc, char **argv)
{
       SIZE_T free_bytes;
       SIZE_T max_free;
       SIZE_T reserved;
       void *system_base;
       bool ok = false;
 
       if (1 == argc)
       {
              ok = Walk(GetCurrentProcess(), free_bytes, max_free, reserved, system_base);
       }
       else
       {
              DWORD pid = atoi(argv[1]);
              HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
              if (0 != hProcess)
              {
                     ok = Walk(hProcess, free_bytes, max_free, reserved, system_base);
                     CloseHandle(hProcess);
              }
       }
       if (ok)
       {
              printf(" 总共可用字节: 0x%I64x/n", (long long) free_bytes);
              printf(" 系统保留字节: 0x%I64x/n", (long long) reserved);
              printf(" 系统基地址: 0x%p/n", system_base);
              printf(" 最大连续可用块: 0x%I64x/n", (long long) max_free);
       }
}
拥抱64位Windows_第1张图片
最让人惊奇的事情是,用 /LARGEADDRESSAWARE选项编译的32位Windows程序,在x64上,实际可访问4GB内存空间。由此可见,在Windows x64上,只需一小点努力,寻址空间就会翻一倍。
还有一个意料之外的结果,在Windows XP SP2上,最大可用块比其他任何一个平台上的都要小。通过在每个基地址处调用GetModuleFileName(),导出DLL在内存中的位置,最终发现UXTheme.DLL在最大可用块的中间(0x5AD70000)有一个隐含的载入地址,你可能会当它当成一个程序错误,但XP x64似乎没有这个问题(基地址:7DF50000),所以重定位XP SP2中的uxtheme.dll可能是安全的。
 
对运行在 x64 之上的 32 位程序, AWE PAE 也是适用的,但 IA64 版的 Windows ,对运行在 Wow64 中的 32 位程序,不提供 AWE PAE 支持。
Wow64环境还有其他的让人惊喜之处,并且带有一组新的API。通常32位程序对%WINDIR%/System32的请求,会被重定向到%WINDIR%/Syswow64中,而且注册表也被虚拟化了,HKLM/Software现在实质上是HKLM/Wow6432Node,这是与HKCU虚拟化的不同之处。例如,Internet Explorer的设置就同时被32位和64位的Internet Explorer共享。(32位应用程序被默认安装于C:/Program Files (x86))。
有一些新的 API ,可用于 32 位程序取消虚拟化时通知 64 位系统,或者检测是否运行在仿真层中;表 2 中的 API 来自最新的 Platform SDK ,要注意的是,它们只在 Windows Server 2003 之后的 kernel32 中实现,“link /delayload:kernel32.dll ”此时已是一个必选项,它通过LoadLibrary()和GetProcAddress(),可使二进制代码同时运行在老版本和新版本的Windows上。在包含函数GetSystemWow64Directory()的winbase.h头文件中,也可找到一个GetProcAddress()的函数原型。如下的程序演示了重定向功能:
拥抱64位Windows_第2张图片
 
#define _WIN32_WINNT 0x0501
#include
#include
#pragma comment(lib, "kernel32.lib")
 
typedef BOOL (__stdcall *ISWOW64PROCESS)(HANDLE hProcess, PBOOL Wow64Process);
typedef BOOL (__stdcall *WOW64ENABLEWOW64FSREDIRECTION)(BOOL Wow64FsEnableRedirection);
 
PGET_SYSTEM_WOW64_DIRECTORY_A pGetSystemWow64DirectoryA = 0;
ISWOW64PROCESS pIsWow64Process = 0;
WOW64ENABLEWOW64FSREDIRECTION pWow64EnableWow64FsRedirection = 0;
 
void ResolveWow64References(void)
{
       HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
       if (hKernel32)
       {
              pGetSystemWow64DirectoryA = (PGET_SYSTEM_WOW64_DIRECTORY_A) GetProcAddress(hKernel32, GET_SYSTEM_WOW64_DIRECTORY_NAME_A_A);
              pIsWow64Process = (ISWOW64PROCESS) GetProcAddress(hKernel32, "IsWow64Process");
              pWow64EnableWow64FsRedirection = (WOW64ENABLEWOW64FSREDIRECTION) GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection");
       }
}
 
void WhatIs(const char *cmd)
{
       DWORD type;
       if (GetBinaryType(cmd, &type))
       {
              switch (type)
              {
                     case SCS_32BIT_BINARY:
                            printf("%s是一个32位二进制文件/n", cmd);
                            break;
                     case SCS_64BIT_BINARY:
                            printf("%s是一个64位二进制文件/n", cmd);
                            break;
                     default:
                            fprintf(stderr, "%s -未知的二进制类型-%d/n", cmd, type);
                            break;
              }
       }
       else
       {fprintf(stderr, "%s - error %d/n", cmd, GetLastError());}
}
 
void main(void)
{
       char windir[MAX_PATH];
       char cmd[MAX_PATH];
       char syswow64[MAX_PATH];
       BOOL bWow64Process = FALSE;
 
       ResolveWow64References();
       if (0 == pWow64EnableWow64FsRedirection)
       {
              fprintf(stderr, "需运行在支持这些API的平台上/n");
              return;
       }
       if (pIsWow64Process)
       {
              pIsWow64Process(GetCurrentProcess(), &bWow64Process);
       }
 
       printf("这%s是一个Wow64进程/n", (bWow64Process ? "" : "不"));
       GetWindowsDirectory(windir, sizeof(windir));
       sprintf(cmd, "%s//system32//cmd.exe", windir);
       printf("启用重定向Wow64FsRedirection/n");
       pWow64EnableWow64FsRedirection(TRUE);
       if (pGetSystemWow64DirectoryA)
       {
              if (pGetSystemWow64DirectoryA(syswow64, sizeof(syswow64)))
              {
                     sprintf(cmd, "%s//cmd.exe", syswow64);
                     WhatIs(cmd);
                     sprintf(cmd, "%s//system32//cmd.exe", windir);
              }
       }
       WhatIs(cmd);
       printf("取消重定向Wow64FsRedirection/n");
       pWow64EnableWow64FsRedirection(FALSE);
       WhatIs(cmd);
}
 
先来看一下在 Wow64 进程当中,是如何包装 32 位寻址空间的。
Windows 检测到 32 位二进制代码中的CreateProcess()调用时,会启用一个Wow64进程来接手,这个Wow64进程实际上是一个有着全部64位寻址空间的64位程序,它加载一个64位的ntdll.dll(在所有64位进程中都如此),也会加载几个转换DLL,这些DLL的任务是从32位堆栈中取出信息,重定向32位的函数调用,且使本地64位函数调用转到64位的ntdll.dll中。
wow64.dll
wow64cpu.dll
wow64mib.dll
wow64win.dll
除了以上这些 DLL ,不会有其他的 64 DLL 加载到 Wow64 的寻址空间中。一旦这个 Wow64 程序被加载,会在 64 位寻址空间中为 32 位进程设置一个区域——你可能会想,肯定在低位的 4GB 中——并且加载 32 位的 ntdll.dll ,接下来就是 32 位程序正常加载到内存中运行了。的确,对一个 32 位进程使用 64 位调试器(如 Windbg ),是有先见性的;当跟踪指令到jmp 33:xxxxxxxx处时,32位调试器只会简单地停下来,而64位的调试器就会跳过这一段,从64位ntdll.dll中进入保护模式继续跟踪。
在很大程度上,%WINDIR%/syswow64目录中的32位DLL,与它们在32位Windows上的副本是相同的,两者之间只有一点性能上的差异,例如32位的ntdll.dll,它看起来不只是对32位程序的本地接口层,还设置好相应的转换工作以便转到64位层,并且自身还带有一些Wow64相关的API,这是在64位版本的ntdll.dll中所没有的。在命令行中键入dumpbin -exports | grep Wow64 | grep Nt之后,显示了一个API列表,让我们来看看它们究竟能做什么:
NtWow64GetNativeSystemInformation
NtWow64QueryInformationProcess64
NtWow64QueryVirtualMemory64
NtWow64ReadVirtualMemory64
 
如果你在 Wow64 层中运行一个 32 位程序,并且想访问 64 位程序中的进程空间,Win32 API ReadProcessMemory()这时可起不了作用,只是读取了寻址空间最开始的4GB。如果你需要的东西是在4GB以外呢?或者想要读取从地址0x7fffffde000开始的进程环境块(PEB)或线程环境块(TEB)呢?
NtQueryInformationProcess()这个函数可返回大多数种类的信息,但返回的数据不能放入一个32位的寻址空间内,转换机制显然会把它替换成零。那么我们怎样收集信息呢?可以创建一些数据结构,并小心地把它们扩展到64位,然后传递给Wow64版本的函数。如下的一个程序演示了如何使用包括GetParentProcessId()和GetCurrentDirectoryExW()在内的这些API。实际上,NtQueryInformationProcess()也可查询到父进程ID,演示程序读取了远程32位或64位进程的进程环境块(PEB),并打印出当前工作目录;由此可看出,一个Wow64进程似乎有一个32位和64位的进程环境块。
 
头文件
nativeinterface.h
 
#pragma once
#include
#include
 
class NTProcessInformation
{
public:
       NTProcessInformation(HANDLE hProcess = GetCurrentProcess());
       NTProcessInformation(DWORD pid);
       bool _declspec(property(get=getok)) ok;
       __w64 ULONG_PTR __declspec(property(get=getpid)) pid;
       __w64 ULONG_PTR __declspec(property(get=getppid)) ppid;
       HANDLE __declspec(property(get=gethprocess)) hProcess;
       bool __declspec(property(get=getusewow64apis)) UseWow64Apis;
       VOID * __ptr64 __declspec(property(get=getpa)) PebBaseAddress;
       inline ULONG_PTR getpid(void) {return x_Pid;}
       inline ULONG_PTR getppid(void) {return x_PPid;}
       inline VOID * __ptr64 getpa(void) {return x_PebAddress;}
       inline bool getok(void) {return x_OK;}
       inline bool getusewow64apis(void) {return x_UseWow64Apis;}
       inline HANDLE gethprocess(void) {return x_hProcess;}
       bool IsWow64Process(HANDLE hProcess = 0);
protected:
       void CommonConstruct(void);
       __w64 ULONG_PTR x_Pid;
       __w64 ULONG_PTR x_PPid;
       DWORD x_ExitStatus;
       VOID * __ptr64 x_PebAddress;
       HANDLE x_hProcess;
       bool x_OK;
       bool x_UseWow64Apis;
};
 
class NTPEB
{
public:
       NTPEB(HANDLE hProcess = GetCurrentProcess());
       NTPEB(DWORD pid);
       NTPEB(NTProcessInformation &ntpi);
       bool _declspec(property(get=getok)) ok;
       bool __declspec(property(get=getusewow64apis)) UseWow64Apis;
       HANDLE __declspec(property(get=gethprocess)) hProcess;
       VOID * __ptr64 __declspec(property(get=getpp)) ProcessParameters;
       inline VOID * __ptr64 getpp(void) {return x_ProcessParameters;}
       inline bool getok(void) {return x_OK;}
       inline bool getusewow64apis(void) {return x_UseWow64Apis;}
       inline HANDLE gethprocess(void) {return x_hProcess;}
protected:
       void CommonConstruct(NTProcessInformation &ntpi);
       VOID * __ptr64 x_ProcessParameters;
       bool x_OK;
       bool x_UseWow64Apis;
       HANDLE x_hProcess;
};
 
class NTProcessParameters
{
public:
       NTProcessParameters(HANDLE hProcess = GetCurrentProcess());
       NTProcessParameters(DWORD pid);
       NTProcessParameters(NTPEB &peb);
       bool _declspec(property(get=getok)) ok;
       std::wstring _declspec(property(get=getcwd)) cwd;
       inline std::wstring getcwd(void) {return x_pwd;}
       inline bool getok(void) {return x_OK;}
protected:
       void CommonConstruct(NTPEB &peb);
       std::wstring x_pwd;
       bool x_OK;
};
 
 
头文件实现文件
nativeinterface.cpp
 
#include
#include
#include "nativeinterface.h"
#include "ntdll_lite.h"
 
typedef BOOL (__stdcall *ISWOW64PROCESS)(HANDLE hProcess, PBOOL Wow64Process);
typedef NTSTATUS (__stdcall *NTQUERYINFORMATIONPROCESS)(IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL);
#if !defined(_WIN64)
typedef NTSTATUS (__stdcall *NTWOW64QUERYINFORMATIONPROCESS64)(IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PSIZE_T64 ReturnLength OPTIONAL);
typedef NTSTATUS (__stdcall *NTWOW64READVIRTUALMEMORY64)( IN HANDLE ProcessHandle, IN PVOID64_ BaseAddress, OUT PVOID Buffer, IN SIZE_T64 BufferSize, OUT PSIZE_T64 NumberOfBytesRead OPTIONAL);
#endif
 
ISWOW64PROCESS pIsWow64Process = 0;
NTQUERYINFORMATIONPROCESS pNtQueryInformationProcess = 0;
#if !defined(_WIN64)
NTWOW64QUERYINFORMATIONPROCESS64 pNtWow64QueryInformationProcess64 = 0;
NTWOW64READVIRTUALMEMORY64 pNtWow64ReadVirtualMemory64 = 0;
#endif
 
static BOOL bLoaded = FALSE;
 
static BOOL LoadDlls(void)
{
       HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
       HMODULE hNtDll = GetModuleHandle("ntdll.dll");
       if ((0 == hKernel32 ) || (0 == hNtDll))
       {return FALSE;}
       pIsWow64Process = (ISWOW64PROCESS) GetProcAddress(hKernel32, "IsWow64Process");
       pNtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS) GetProcAddress(hNtDll, "NtQueryInformationProcess");
#if !defined(_WIN64)
       pNtWow64QueryInformationProcess64 = (NTWOW64QUERYINFORMATIONPROCESS64) GetProcAddress(hNtDll, "NtWow64QueryInformationProcess64");
       pNtWow64ReadVirtualMemory64 = (NTWOW64READVIRTUALMEMORY64) GetProcAddress(hNtDll, "NtWow64ReadVirtualMemory64");
#endif
       bLoaded = TRUE;
       return (0 != pNtQueryInformationProcess);
}
 
NTProcessInformation::NTProcessInformation(HANDLE hProcess)
:      x_Pid(-1)
,      x_PPid(-1)
,      x_ExitStatus(STILL_ACTIVE)
,      x_PebAddress(0)
,      x_hProcess(hProcess)
,      x_OK(false)
,      x_UseWow64Apis(false)
{
       CommonConstruct();
}
 
NTProcessInformation::NTProcessInformation(DWORD pid)
:      x_Pid(-1)
,      x_PPid(-1)
,      x_ExitStatus(STILL_ACTIVE)
,      x_PebAddress(0)
,      x_hProcess(0)
,      x_OK(false)
,      x_UseWow64Apis(false)
{
       x_hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
       CommonConstruct();
}
 
void NTProcessInformation::CommonConstruct(void)
{
       if (!bLoaded)
       {
              if (!LoadDlls())
              {return;}
       }
       DWORD err;
       PROCESS_BASIC_INFORMATION info;
       ULONG realsize;
 
       if (ERROR_SUCCESS != (err = pNtQueryInformationProcess(x_hProcess, ProcessBasicInformation, (PVOID) &info, sizeof(info), &realsize)))
       {return;}
       if (0 == info.PebBaseAddress)
       {
#if defined(_WIN64)
              return;
#else
              PROCESS_BASIC_INFORMATION64 info64;
              SIZE_T64 realsize64;
 
              if (!IsWow64Process(GetCurrentProcess()))
              {return;}
              if (0 == pNtWow64QueryInformationProcess64)
              {return;}
              if (ERROR_SUCCESS != (err = pNtWow64QueryInformationProcess64(x_hProcess, ProcessBasicInformation, (PVOID) &info64, sizeof(info64), &realsize64)))
              {return;}
              x_Pid = (ULONG_PTR) info64.UniqueProcessId;
              x_PPid = (ULONG_PTR) info64.InheritedFromUniqueProcessId;
              x_ExitStatus = info64.ExitStatus;
              x_PebAddress = info64.PebBaseAddress;
              x_OK = true;
              x_UseWow64Apis = true;
#endif
       }
       else
       {
              x_Pid = info.UniqueProcessId;
              x_PPid = info.InheritedFromUniqueProcessId;
              x_ExitStatus = info.ExitStatus;
              x_PebAddress = info.PebBaseAddress;
              x_OK = true;
       }
}
 
bool NTProcessInformation::IsWow64Process(HANDLE hProcess)
{
       if (pIsWow64Process)
       {
              BOOL IsWow64 = FALSE;
 
              if (pIsWow64Process(hProcess ? hProcess : x_hProcess, &IsWow64))
              {return IsWow64 ? true : false;}
       }
       return false;
}
 
NTPEB::NTPEB(HANDLE hProcess)
:      x_ProcessParameters(0)
,      x_OK(false)
,      x_UseWow64Apis(false)
,      x_hProcess(0)
{
       NTProcessInformation info(hProcess);
       CommonConstruct(info);
}
 
NTPEB::NTPEB(DWORD pid)
:      x_ProcessParameters(0)
,      x_OK(false)
,      x_UseWow64Apis(false)
,      x_hProcess(0)
{
       NTProcessInformation info(pid);
       CommonConstruct(info);
}
 
NTPEB::NTPEB(NTProcessInformation &ntpi)
:      x_ProcessParameters(0)
,      x_OK(false)
,      x_UseWow64Apis(false)
,      x_hProcess(0)
{
       CommonConstruct(ntpi);
}
 
void NTPEB::CommonConstruct(NTProcessInformation &ntpi)
{
       if (!bLoaded)
       {
              if (!LoadDlls())
              {return;}
       }
       x_hProcess = ntpi.hProcess;
       if (ntpi.UseWow64Apis)
       {
#if defined(_WIN64)
              return;
#else
              PEB64 peb64;
              SIZE_T64 realsize64;
              NTSTATUS err;
              if (ERROR_SUCCESS != (err = pNtWow64ReadVirtualMemory64(x_hProcess, ntpi.getpa(), &peb64, sizeof(PEB64), &realsize64)))
              {return;}
              x_UseWow64Apis = true;
              x_ProcessParameters = peb64.ProcessParameters;
#endif
       }
       else
       {
              PEB peb;
              SIZE_T realsize;
              if (FALSE == ReadProcessMemory(x_hProcess, Ptr64ToPtr(ntpi.getpa()), &peb, sizeof(PEB), &realsize))
              {return;}
              x_ProcessParameters = peb.ProcessParameters;
       }
       x_OK = true;
}
 
NTProcessParameters::NTProcessParameters(HANDLE hProcess)
:      x_OK(false)
{
       NTPEB peb(hProcess);
       CommonConstruct(peb);
}
 
NTProcessParameters::NTProcessParameters(DWORD pid)
:      x_OK(false)
{
       NTPEB peb(pid);
       CommonConstruct(peb);
}
 
NTProcessParameters::NTProcessParameters(NTPEB &peb)
:      x_OK(false)
{
       CommonConstruct(peb);
}
 
void NTProcessParameters::CommonConstruct(NTPEB &peb)
{
       if (!bLoaded)
       {
              if (!LoadDlls())
              {return;}
       }
       if (peb.UseWow64Apis)
       {
#if defined(_WIN64)
              return;
#else
              PROCESS_PARAMETERS64 ProcParams64;
              SIZE_T64 realsize64;
              NTSTATUS err;
 
              if (ERROR_SUCCESS != (err = pNtWow64ReadVirtualMemory64(peb.hProcess, peb.getpp(), &ProcParams64, sizeof(PROCESS_PARAMETERS64), &realsize64)))
              {return;}
              SIZE_T64 len = ProcParams64.CurrentDirectory.DosPath.Length;
              wchar_t *buffer = new wchar_t[(SIZE_T) (len+1)];
              if (ERROR_SUCCESS != (err = pNtWow64ReadVirtualMemory64(peb.hProcess, ProcParams64.CurrentDirectory.DosPath.Buffer, buffer, len * sizeof(wchar_t), &realsize64)))
              {return;}
              buffer[realsize64/2] = '/0';
              x_pwd = buffer;
              delete [] buffer;
#endif
       }
       else
       {
              PROCESS_PARAMETERS ProcParams;
              SIZE_T realsize;
 
              if (FALSE == ReadProcessMemory(peb.hProcess, Ptr64ToPtr(peb.getpp()), &ProcParams, sizeof(PROCESS_PARAMETERS), &realsize))
              {return;}
              SIZE_T len = ProcParams.CurrentDirectory.DosPath.Length;
              wchar_t *buffer = new wchar_t[len+1];
              if (FALSE == ReadProcessMemory(peb.hProcess, ProcParams.CurrentDirectory.DosPath.Buffer, buffer, len*sizeof(wchar_t), &realsize))
              {return;}
              buffer[realsize/2] = '/0';
              x_pwd = buffer;
              delete [] buffer;
       }
       x_OK = true;
}
 
 
相关头文件
ntdll_lite.h
 
#pragma once
typedef LONG NTSTATUS;
typedef LONG KPRIORITY;
 
#if (_MSC_VER < 1300)
typedef ULONG *ULONG_PTR;
#endif
 
typedef enum _PROCESSINFOCLASS
{
    ProcessBasicInformation,
    ProcessQuotaLimits,
    ProcessIoCounters,
    ProcessVmCounters,
    ProcessTimes,
    ProcessBasePriority,
    ProcessRaisePriority,
    ProcessDebugPort,
    ProcessExceptionPort,
    ProcessAccessToken,
    ProcessLdtInformation,
    ProcessLdtSize,
    ProcessDefaultHardErrorMode,
    ProcessIoPortHandlers,
    ProcessPooledUsageAndLimits,
    ProcessWorkingSetWatch,
    ProcessUserModeIOPL,
    MaxProcessInfoClass
} PROCESSINFOCLASS;
 
typedef PVOID PPEB_LDR_DATA;
typedef PVOID PPEB_FREE_BLOCK;
 
#if !defined(_WIN64)
#if (_MSC_VER < 1300)
typedef unsigned __int64 PCHAR64;
typedef unsigned __int64 HANDLE64;
typedef unsigned __int64 PVOID64_;
#else
typedef char * __ptr64 PCHAR64;
typedef void * __ptr64 HANDLE64;
typedef void * __ptr64 PVOID64_;
#endif
typedef PVOID64_ PPEB_LDR_DATA64;
typedef PVOID64_ PPEB_FREE_BLOCK64;
typedef unsigned __int64 SIZE_T64, *PSIZE_T64;
#endif
 
typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
 
#if !defined(_WIN64)
typedef struct _UNICODE_STRING64 {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR64 Buffer;
} UNICODE_STRING64;
typedef UNICODE_STRING64 *PUNICODE_STRING64;
#endif
 
typedef struct _CURRENT_DIRECTORY {
    UNICODE_STRING DosPath;
    HANDLE Handle;
} CURRENT_DIRECTORY, *PCURRENT_DIRECTORY;
 
#if !defined(_WIN64)
typedef struct _CURRENT_DIRECTORY64 {
    UNICODE_STRING64 DosPath;
    HANDLE64 Handle;
} CURRENT_DIRECTORY64, *PCURRENT_DIRECTORY64;
#endif
 
typedef struct _PROCESS_PARAMETERS {
    ULONG MaximumLength;
    ULONG Length;
    ULONG Flags;
    ULONG DebugFlags;
    HANDLE ConsoleHandle;
    ULONG ConsoleFlags;
    HANDLE StandardInput;
    HANDLE StandardOutput;
    HANDLE StandardError;
    CURRENT_DIRECTORY CurrentDirectory; 
    UNICODE_STRING DllPath;
    UNICODE_STRING ImagePathName;
    UNICODE_STRING CommandLine;
    PVOID Environment;
    ULONG StartingX;
    ULONG StartingY;
    ULONG CountX;
    ULONG CountY;
    ULONG CountCharsX;
    ULONG CountCharsY;
    ULONG FillAttribute;
    ULONG WindowFlags;
    ULONG ShowWindowFlags;
    UNICODE_STRING WindowTitle;
    UNICODE_STRING DesktopInfo;
    UNICODE_STRING ShellInfo;
    UNICODE_STRING RuntimeData;
} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;
 
#if !defined(_WIN64)
typedef struct _PROCESS_PARAMETERS64 {
    ULONG MaximumLength;
    ULONG Length;
    ULONG Flags;
    ULONG DebugFlags;
    HANDLE64 ConsoleHandle;
    ULONG ConsoleFlags;
    HANDLE64 StandardInput;
    HANDLE64 StandardOutput;
    HANDLE64 StandardError;
    CURRENT_DIRECTORY64 CurrentDirectory;
    UNICODE_STRING64 DllPath;
    UNICODE_STRING64 ImagePathName;
    UNICODE_STRING64 CommandLine;
    PVOID64_ Environment;
    ULONG StartingX;
    ULONG StartingY;
    ULONG CountX;
    ULONG CountY;
    ULONG CountCharsX;
    ULONG CountCharsY;
    ULONG FillAttribute;
    ULONG WindowFlags;
    ULONG ShowWindowFlags;
    UNICODE_STRING64 WindowTitle;
    UNICODE_STRING64 DesktopInfo;
    UNICODE_STRING64 ShellInfo;
    UNICODE_STRING64 RuntimeData;
} PROCESS_PARAMETERS64
#if (_MSC_VER < 1300)
;
typedef unsigned __int64 PPROCESS_PARAMETERS64;
#else
, *PPROCESS_PARAMETERS64;
#endif
#endif
 
typedef struct _PEB {
    BOOLEAN InheritedAddressSpace;
    HANDLE Mutant;              
    PVOID ImageBaseAddress;
    PPEB_LDR_DATA Ldr;
    PPROCESS_PARAMETERS ProcessParameters;
    PVOID SubSystemData;
    PVOID ProcessHeap;
    PVOID FastPebLock;
    PVOID FastPebLockRoutine;
    PVOID FastPebUnlockRoutine;
    PVOID Spare[4];
    PPEB_FREE_BLOCK FreeList;
    ULONG TlsExpansionCounter;
    PVOID TlsBitmap;
    ULONG TlsBitmapBits[2];
    PVOID ReadOnlySharedMemoryBase;
    PVOID ReadOnlySharedMemoryHeap;
    PVOID *ReadOnlyStaticServerData;
    PVOID AnsiCodePageData;
    PVOID OemCodePageData;
    PVOID UnicodeCaseTableData;
    LARGE_INTEGER CriticalSectionTimeout;
} PEB, *PPEB;
 
#if !defined(_WIN64)
typedef struct _PEB64 {
    BOOLEAN InheritedAddressSpace;
    HANDLE64 Mutant;            
    PVOID64_ ImageBaseAddress;
    PPEB_LDR_DATA64 Ldr;
#if (_MSC_VER < 1300)
    PPROCESS_PARAMETERS64 ProcessParameters;
#else
    PROCESS_PARAMETERS64 *__ptr64 ProcessParameters;
#endif
    PVOID64_ SubSystemData;
    PVOID64_ ProcessHeap;
    PVOID64_ FastPebLock;
    PVOID64_ FastPebLockRoutine;
    PVOID64_ FastPebUnlockRoutine;
    PVOID64_ Spare[4];
    PPEB_FREE_BLOCK64 FreeList;
    ULONG TlsExpansionCounter;
    PVOID64_ TlsBitmap;
    ULONG TlsBitmapBits[2];
    PVOID64_ ReadOnlySharedMemoryBase;
    PVOID64_ ReadOnlySharedMemoryHeap;
    PVOID64_ *ReadOnlyStaticServerData;
    PVOID64_ AnsiCodePageData;
    PVOID64_ OemCodePageData;
    PVOID64_ UnicodeCaseTableData;
    LARGE_INTEGER CriticalSectionTimeout;
} PEB64
#if (_MSC_VER < 1300)
;
typedef unsigned __int64 PPEB64;
#else
, * __ptr64 PPEB64;
#endif
#endif
 
typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG_PTR AffinityMask;
    KPRIORITY BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;
 
#if !defined(_WIN64)
typedef struct _PROCESS_BASIC_INFORMATION64 {
    NTSTATUS ExitStatus;
    PPEB64 PebBaseAddress;
    ULONG64 AffinityMask;
    KPRIORITY BasePriority;
    ULONG64 UniqueProcessId;
    ULONG64 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION64;
typedef PROCESS_BASIC_INFORMATION64 *PPROCESS_BASIC_INFORMATION64;
#endif
 
 
主程序
#include
#include "nativeinterface.h"
#define DEBUG
 
BOOL GetCurrentDirectoryExW(HANDLE hProcess, wchar_t *buffer, SIZE_T buflen)
{
       NTProcessParameters params(hProcess);
       if (!params.ok)
       {return false;}
 
       std::wstring result = params.cwd;
       SIZE_T len = min(buflen-1, result.length());
       memcpy(buffer, result.c_str(), sizeof(wchar_t) * len);
       buffer[len] = L'/0';
       return TRUE;
}
 
ULONG_PTR GetParentProcessIdEx(HANDLE hProcess)
{
       NTProcessInformation info(hProcess);
      if (!info.ok)
       {return -1;}
       return info.ppid;
}
 
BOOL MyIsWow64Process(HANDLE hProcess)
{
       NTProcessInformation info(hProcess);
       if (!info.ok)
       {return FALSE;}
       return info.IsWow64Process() ? TRUE : FALSE;
}
 
#if defined(DEBUG)
void main(int argc, char **argv)
{
       HANDLE hProcess;
       if (1 == argc)
       {hProcess = GetCurrentProcess();}
       else
       {
              DWORD pid = atoi(argv[1]);
              hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
              if (0 == hProcess)
              {return;}
       }
       wchar_t path[4096];
       printf("ppid = %d/n", GetParentProcessIdEx(hProcess));
       printf(" %s 是一个 Wow64 进程 /n", (MyIsWow64Process(hProcess) ? "" : " "));
       GetCurrentDirectoryExW(hProcess, path, sizeof(path));
       printf(" 当前工作目前 = %ls/n", path);
}
#endif
 
可读取 64 位寻址空间——这意味着不但能读取其他 64 位进程的进程环境块( PEB ),而且还能观察到 Wow64 进程的 64 位部分。如NtWow64WriteVirtualMemory64()这些分配和修改虚拟内存的函数一样,可加载一个64位DLL到Wow64进程的64位部分。
Wow64程序面对的是虚拟化后的64位文件系统和注册表,但从一个32位系统发起的网络应用,如//server/admin$和ConnectRegistry(),可能面对的是一个真实的64位文件系统和注册表,所以,基于这些原因,copy 1234.dll //server/admin$/system32这行命令可能会达不到预期的目的。
 
 
本地64 位移植
Windows 平台上,需要多于 4GB 内存的程序越来越多,这些程序要么在 AWE PAE 上苦苦挣扎,要么转而栖身到其他的 64 位平台,如 Solaris 。以下列出的一些原因,可能会使你选择重新编译生成本地 64 位二进制文件。
 
无论是静态还是动态的 32 位库,都不能链接到 64 位的可执行文件,反之亦然,但是如果提供中间件,或者计划部署到 64 Windows 上,那就需要移植了——最初我们是移植到 IA64 上,现在是 x64 。基于 COM DLL 在此是个例外,它们在 32 位的宿主中已经被代理了,即使通过网络连接时也是如此,但最终你会发现由此带来的进程内性能损失得不偿失,最终还是要把组件移植到 64 Windows 上。
如果你要发布一个Internet Explorer的插件,将会发现,32位版本的插件在64位Internet Explorer上无法使用。64位的Windows同时带有32位和64位的Internet Explorer,并且是按照每个窗口一个进程这样配置的,所以在64位平台上两者都能运行。因此,对64位Windows,必须同时发布32位和64位的插件。
 
如果有一个 16 位的 Windows 程序,因为没有Windows on Windows on Windows (WoWoW?)诸如的方案,所以16位程序不可能运行在x64上(会有诸如提示:映像文件有效,但不适用于此计算机类型)。
设备驱动程序也亦然,32位的设备驱动程序无法工作在64位的Windows上。
也许移植的目标是 IA64 ,但会发现,程序并没有预期中的性能,或者是因为程序使用了 AWE PAE ,两者在 IA64 平台上均不被支持,但看起来似乎本地编译的 IA64 二进制文件比 32 位效率要高得多。此现象主要针对第一代安腾处理器,对于第二代安腾处理器,情况要好多了。或许 Windows 使用的是 32 位软件二进制仿真器,而不是本地硬件仿真器。
 
移植到新平台上,工作量非常巨大,一项普通的移植都需要严格的计划和时间上巨大的开销。为了找到问题的范围,无论如何都要挑出一些有代表性的源代码,试编译运行。听起来好像很简单,只是敲入 make (或者nmake、devenv /build )就行了,实际上,要做的工作远远不只这些。
那些没有源代码的程序,有库可用吗?在移植中要使用什么平台呢?是使用目标平台还是类似 x86 的桌面平台?对新平台来说,源代码配置管理系统( SCM )可用吗?从哪可得到编译器和链接器?所有的工具都适合 64 位开发吗?构建环境是否支持在同一源代码中生成多平台应用,还是需要多做一份拷贝?实际上,敲入 make 需要更多的前端工作,但最简单的解决方案是,拷贝一份源代码,安装好Microsoft Platform SDK,使用x86 to x64跨平台交叉编译器,而把其他诸如开发环境、生成工具、SCM等等,统统扔在32位Windows中不管。这也许是最大的一个惊喜了,在链接可执行文件前,甚至都不需要一台64位电脑。
正因为不能把32位的静态或动态库,链接到64位程序,所以,如果没有全部库的源代码,或者64位版本的库还不存在,那么移植方案就得暂时搁下。你需要在64位目标平台上找到一些东西,替换掉这些库,要么说服生产商提供这些库,或者干脆不移植。
 
接下来一个最大的问题是开发环境——包括编译器、链接器、调试器、源代码管理系统、 make 和集成开发环境( IDE )。
从哪可以得到一个编译器呢?在本文写作时,GNU C/C++还不行。Intel已经发布了商业版本的编译器,分别对应于IA64(本地或跨平台)、IA32E(Intel称其为EM64T x64平台)和IA32;微软也有一对版本号为14.0的编译器(VC.NET 2005),分别对应于x86 to IA64和x64,而通过Platform SDK,提供了跨平台交叉编译功能。实际上在Visual Studio.NET 2005中,微软已经提供了本地x64和IA64编译器。
集成开发环境(IDE)不需要是64位的,你能在32位系统上跨平台交叉编译,或者在64位平台上运行32的IDE。
从哪可以得到一个调试器呢? Windbg 有针对以上三个平台的版本,但对普通人来说,它还是有点不太好用,除非是黑客。Visual Studio.NET 2005有一个远程调试器服务端可用于以上两个64位平台。它仍是一个32位程序,所以你能得到一个混合的实现,在当前x86桌面系统上开发,而在64位平台上远程调试。
那配置管理系统呢?也许你的SCM已经支持相应64位平台开发了,如果没有,那只有在32位系统上跨平台交叉编译,或者拷贝源代码到64位平台上。
 
现在我们可以准备生成程序了,所有为移植做的准备就是为了现在享受胜利的果实,但代码有多具移植性呢?
我们都知道sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long),但也要准备接受sizeof(long) != sizeof(void *)或者sizeof(int) != sizeof(void *)。大多数64位Unix系统定义sizeof(long) == sizeof(void *),这被称作LP64模型,此时long与指针都是64位的。Windows选择的是LLP64模型,此时对移植32位Windows源代码来说,sizeof(long long) == sizeof(void *)。这意味着,如果代码中把指针赋值给int(这是不可移植的,因为它不严谨且假定sizeof(int) == sizeof(long) == sizeof(void *)),或者把指针赋值给long,那就要在代码移植之前,把它们全部改正过来。记住,程序看起来也许运行得很正常,只要它位于64位寻址空间的低4GB位置,但malloc()和HeapAlloc()返回的值可能会在此之外,MapViewOfFile()也同样。
 
在很大程度上,编写良好的Win32代码只需稍作改动,便可通过编译。你可能会惊奇地发现,在很多地方,DWORD与PVOID几乎是同一样东西,只要认真遵循前面所提到的方法,源代码通过编译不是件难事,但是,记住在生成64位程序时使用-W3选项,对编译器的警告多加留意。
 
就以往的经验来说,需要为每一个 COM 组件生成一个新的 GUID ,但在 Windows x64 上,同一个组件的 32 位和 64 位版本(如Internet Explorer 工具栏 )可以共享一个 GUID ,并可在同一台电脑上,同时为它们的客户程序提供服务。事实上, COM 的实现是高度可伸缩性的,可以允许 32 位的组件被 64 位进程使用(反之亦然),或同时注册 32 位和 64 位的组件。换句话来说,如果你选择移植组件,可以保留 GUID 和接口定义;如果不移植(那些充当进程内服务的 IE 工具栏和插件必须要移植),多数情况下,它们都能正常运行,此时性能可能会受影响。
 
一些更棘手的问题都是围绕与 32 位程序共享二进制数据上,也许是通过文件,或者是共享内存、网络等等。在此有很多技术都可以用得上,但微软已经在它的编译器和头文件中内置了一些实用的工具。
微软编译器 cl 版本 13.0 或者更高(VS.NET 2003或后续版本 ),Platform SDK中的14.0版本,这些编译器都支持/Wp64选项。对每个源程序,只需简单地设置/Wp64选项,就可以看到5个(至少到目前为止)有用的警告;参见图1;以下的程序示范了这些警告,并且提供了一些有关移植的解决方法。
拥抱64位Windows_第3张图片
 
#include
#include
#include
#include
#include
#pragma comment(lib, "user32.lib")
 
void main(void)
{
       void *ptr = 0;
       int val;
       intptr_t intptr;
       size_t sizet;
       HWND hWnd = GetDesktopWindow();
       printf("%x/n", ptr);
       printf("%p/n", ptr);
       val = (int) ptr;
       intptr = (intptr_t) ptr;
       val = strlen("abc");
       sizet = strlen("abc");
       val = (int) hWnd;
       val = HandleToUlong(hWnd);
       hWnd = (HWND) UlongToHandle(val);
}
 
在头文件 BaseTsd.h 中,有一些可用的新内联函数和宏,如DWORD32、DWORD64、UINT_PTR、PointerToUlong()、Ptr64ToPtr()等。HWND在64位Windows上已是一个64位的变量,如果你想把它传递给一个32位进程,它的值会被截断,HandleToUlong()此时正好帮得上忙,但需注意的是,尽量不要让截断发生。*__ptr64强制一个指针变量占用64位,哪怕是在32位平台上,void * __ptr64 ptr就可强制在32位和64位平台上得到同样大小的变量,以保证二进制兼容;还有一个相应的__ptr32。显然,在我们发现8TB寻址空间不够用之前,是不会出现__ptr128的。
 
在把指针赋值给int时,尽量使用intptr_t,而不是int或long long。大多数编译器都提供了一个typedef,以保证对你的目标平台,变量有一个正确的大小。
 
删除源代码中的内联汇编语句,或者用一些“ C ”代码代替它,在生成程序时,也不要链接外部的汇编文件,因为__asm在IA64和x64编译器中会产生错误。为了确保二进制的兼容,请明智地决定联合体(Union)中变量的位置,以便结构数据对齐保持一致。参见以下程序:
 
#include
#include
#include
 
struct abc
{
       HWND AWindow;
       void * __ptr32 ThirtyTwoBitPointer;
       void * __ptr64 SixtyFourBitPointer;
       long ALong;
};
 
struct def
{
       union
       {
              HWND AWindow;
              long long reserved;
       };
       union
       {
              void * __ptr32 ThirtyTwoBitPointer;
              long long reserved2;
       };
       void * __ptr64 SixtyFourBitPointer;
       union
       {
              long ALong;
              long long reserved3;
       };
};
 
void main(void)
{
       printf("%d/n", sizeof(abc));
       printf("/tAWindow %p/n", offsetof(abc, AWindow));
       printf("/t32bit pointer %p/n", offsetof(abc, ThirtyTwoBitPointer));
       printf("/t64bit pointer %p/n", offsetof(abc, SixtyFourBitPointer));
       printf("/tAlong %p/n", offsetof(abc, ALong));
 
       printf("%d/n", sizeof(def));
       printf("/tAWindow %p/n", offsetof(def, AWindow));
       printf("/t32bit pointer %p/n", offsetof(def, ThirtyTwoBitPointer));
       printf("/t64bit pointer %p/n", offsetof(def, SixtyFourBitPointer));
       printf("/tAlong %p/n", offsetof(def, ALong));
}
 
IA64 平台,像多数 RISC 处理器一样,对数据对齐过分吹毛求疵,有以下代码:
unsigned char *ptr;
DWORD dword = (DWORD) (*(unsigned long long *) ptr);
此时使用一个memcpy(),也许是比赋值更好的选择。
 
也许你经常要为 32 位和 64 位生成不同代码,为此,微软的 64 位编译器定义了一个预处理变量_WIN64。微软的编译器同时也提供_M_IX86、_M_IA64、_M_AMD64,以便应付那些需要针对特定处理器生成代码的极端场合。
 
64位移植中最困难的事,恐怕就是代码生成环境了。如果你之前移植到其他的架构上(如Alpha、MIPS或PowerPC),可能会觉得很容易,平台提供的示例代码中,常含有一个调用cl和nmake的makefile,以便演示在不同平台上针对同一源代码如何编译,其思想是把平台与输出分离。但即使是把vc?.pdb文件放在正确位置,并且也把编译器临时目录分离,makefile在多平台同时生成程序时,还是会有问题。
如果你正在使用Visual Studio IDE,可以很容易地导出配置文件、指定输出目录$(OUTDIR)、按需调整好编译器,然后在目标平台上打开一个Platform SDK命令行窗口,用如下命令devenv /build /useenv myproject.sln "Win32 - Debug(X64)"来构建一个工程。可以考虑一下最新的VS.NET 2005和它的跨平台交叉编译器,只要在每一个配置中指定目标平台,就可以很容易地生成应用(或工程)。在AMD的官方网站上,有一个示例,演示了如何使用Visual Studio和Platform SDK,从现有的工程文件中,生成64位二进制程序。
 
最后当然还要说一下软件安装的问题,如果有一个面向32位、64位(IA64和x64)的混合安装程序,在一开始就需要仔细地计划。如果使用Windows Installer,那么针对每一个平台架构,都会有一个MSI文件。有趣的是,在64位的安装中,可使用32位的用户定制(Custom Action)DLL,这暗示着在此之下,有一对协同工作的32位和64位进程。但这不在本文讨论范畴之内。
 
 
结论
不是每个人都需要把他们的程序移植到本地64位平台上,事实上,如果依然作为运行在64位处理器上的32位程序,可能会更加小巧,方便部署——当然除了安腾处理器。目前唯一清楚的是,微软为它的32位仿真层做了大量卓越的工作,而64位处理器和主板,价格便宜量又足,它们会越来越受到欢迎,因此,不管是运行现有32位程序、或是新游戏、还是复杂的商业程序,我们都没有理由——不拥抱64位Windows!!

你可能感兴趣的:(64位,windows,parameters,编译器,string,平台,buffer)