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文件。这就是重定向的干预结果。
Wow64DisableWow64FsRedirection // 关闭重定向 deletefile('c:/windows/system32/a.txt') ; Wow64RevertWow64FsRedirection // 恢复重定向以上代码,就有效的删除了system32目录里面的a.txt文件,而不是syswow64目录中的a.txt文件。
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
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。其实为
什么会错误呢?MSDN上给出了说明:
即应该使用GetNativeSystemInfo函数,使用GetSystemInfo可能不太准确,代码如下:
SYSTEM_INFO si; GetNativeSystemInfo(&si);= if((si.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_IA64) || (si.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64) //64 bit; else //32 bit.下面是MSDN上给出的较完整的代码,在调用不到 GetNativeSystemInfo的时候,才使用 GetSystemInfo:
#include <windows.h> #include <tchar.h> #include <stdio.h> #define BUFSIZE 80 typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); int __cdecl _tmain() { OSVERSIONINFOEX osvi; SYSTEM_INFO si; PGNSI pGNSI; BOOL bOsVersionInfoEx; ZeroMemory(&si, sizeof(SYSTEM_INFO)); ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); // Try calling GetVersionEx using the OSVERSIONINFOEX structure. // If that fails, try using the OSVERSIONINFO structure. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) { osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) return FALSE; } // Call GetNativeSystemInfo if supported // or GetSystemInfo otherwise. pGNSI = (PGNSI) GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); if(NULL != pGNSI) pGNSI(&si); else GetSystemInfo(&si); switch (osvi.dwPlatformId) { // Test for the Windows NT product family. case VER_PLATFORM_WIN32_NT: // Test for the specific product. if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) printf ("Windows Vista "); else printf ("Windows Server \"Longhorn\" " ); } if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) { if( GetSystemMetrics(SM_SERVERR2) ) printf( "Microsoft Windows Server 2003 \"R2\" "); else if( osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) { printf( "Microsoft Windows XP Professional x64 Edition "); } else printf ("Microsoft Windows Server 2003, "); } if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) printf ("Microsoft Windows XP "); if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) printf ("Microsoft Windows 2000 "); if ( osvi.dwMajorVersion <= 4 ) printf ("Microsoft Windows NT "); // Test for specific product on Windows NT 4.0 SP6 and later. if( bOsVersionInfoEx ) { // Test for the workstation type. if ( osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture!=PROCESSOR_ARCHITECTURE_AMD64) { if( osvi.dwMajorVersion == 4 ) printf ( "Workstation 4.0 " ); else if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) printf ( "Home Edition " ); else printf ( "Professional " ); } // Test for the server type. else if ( osvi.wProductType == VER_NT_SERVER || osvi.wProductType == VER_NT_DOMAIN_CONTROLLER ) { if(osvi.dwMajorVersion==5 && osvi.dwMinorVersion==2) { if ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) { if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) printf ( "Datacenter Edition " "for Itanium-based Systems" ); else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) printf ( "Enterprise Edition " "for Itanium-based Systems" ); } else if ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) { if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) printf ( "Datacenter x64 Edition " ); else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) printf ( "Enterprise x64 Edition " ); else printf( "Standard x64 Edition " ); } else { if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) printf ( "Datacenter Edition " ); else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) printf ( "Enterprise Edition " ); else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) printf ( "Web Edition " ); else printf ( "Standard Edition " ); } } else if(osvi.dwMajorVersion==5 && osvi.dwMinorVersion==0) { if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) printf ( "Datacenter Server " ); else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) printf ( "Advanced Server " ); else printf ( "Server " ); } else // Windows NT 4.0 { if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) printf ("Server 4.0, Enterprise Edition " ); else printf ( "Server 4.0 " ); } } } // Test for specific product on Windows NT 4.0 SP5 and earlier else { HKEY hKey; TCHAR szProductType[BUFSIZE]; DWORD dwBufLen=BUFSIZE*sizeof(TCHAR); LONG lRet; lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\" "ProductOptions"), 0, KEY_QUERY_VALUE, &hKey ); if( lRet != ERROR_SUCCESS ) return FALSE; lRet = RegQueryValueEx( hKey, TEXT("ProductType"), NULL, NULL, (LPBYTE) szProductType, &dwBufLen); RegCloseKey( hKey ); if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE*sizeof(TCHAR)) ) return FALSE; if ( lstrcmpi( TEXT("WINNT"), szProductType) == 0 ) printf( "Workstation " ); if ( lstrcmpi( TEXT("LANMANNT"), szProductType) == 0 ) printf( "Server " ); if ( lstrcmpi( TEXT("SERVERNT"), szProductType) == 0 ) printf( "Advanced Server " ); printf( "%d.%d ", osvi.dwMajorVersion, osvi.dwMinorVersion ); } // Display service pack (if any) and build number. if( osvi.dwMajorVersion == 4 && lstrcmpi( osvi.szCSDVersion, TEXT("Service Pack 6") ) == 0 ) { HKEY hKey; LONG lRet; // Test for SP6 versus SP6a. lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\" "Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey ); if( lRet == ERROR_SUCCESS ) printf( "Service Pack 6a (Build %d)\n", osvi.dwBuildNumber & 0xFFFF ); else // Windows NT 4.0 prior to SP6a { _tprintf( TEXT("%s (Build %d)\n"), osvi.szCSDVersion, osvi.dwBuildNumber & 0xFFFF); } RegCloseKey( hKey ); } else // not Windows NT 4.0 { _tprintf( TEXT("%s (Build %d)\n"), osvi.szCSDVersion, osvi.dwBuildNumber & 0xFFFF); } break; // Test for the Windows Me/98/95. case VER_PLATFORM_WIN32_WINDOWS: if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) { printf ("Microsoft Windows 95 "); if (osvi.szCSDVersion[1]=='C' || osvi.szCSDVersion[1]=='B') printf("OSR2 " ); } if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) { printf ("Microsoft Windows 98 "); if ( osvi.szCSDVersion[1]=='A' || osvi.szCSDVersion[1]=='B') printf("SE " ); } if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) { printf ("Microsoft Windows Millennium Edition\n"); } break; case VER_PLATFORM_WIN32s: printf ("Microsoft Win32s\n"); break; } return TRUE; }
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了。真是杯具,我为我的哨子付出了太大的代价。