當前計算機系統已經逐漸地從32位轉到64位,XP,2003,VISTA都有64位的版本。從目前而言,32位應用程序還是佔了絕大多數,但是也有部分應用程序既有32位版本,又有64位版本。爲了保證32位程序可以順利運行在64位系統上,微軟提供了一套叫WOW64的模擬機制。通常把這套系統稱爲WOW64。從總體上來說,WOW64是一套基於用戶模式的動態鏈接庫,它可以把32位應用程序的發出的命令翻譯成64位系統可以接受的格式。從下圖中可以大概地看出32位應用程序運行在64位系統上的方式。
當32位應用程序運行的時候,首先會去啓動本地庫加載器(Native Library Loader)。加載器會識別出應用程序是32位的並且用特殊的方式來處理它。加載器會爲32位應用程序建立起一個WOW64的模擬環境並把控制權交給32位的Ntdll.dll。運行在32位應用程序和64位Ntdll.dll之間的WOW64模擬環境會將32位應用程序的指令翻譯成64位Ntdll.dll可以接受的方式,並且它也可以把系統的指令翻譯成32位應用程序可以接受的方式。
2 如何判斷系統是64位
判斷系統是否是64位的方法有很多。對於C#來說,調用WMI是一種簡單易行的方式。我們可以用Win32_Processor類裏面的AddressWidth屬性來表示系統的位寬。AddressWidth的值受CPU和操作系統的雙重影響。具體的值如下面的表格所示:
32bit OS | 64bit OS | |
32bit CPU | AddressWidth = 32 | N/A |
64bit CPU | AddressWidth = 32 | AddressWidth = 64 |
可以用下面的C#代碼得到AddressWidth的值
{
ConnectionOptions oConn = new ConnectionOptions();
System.Management.ManagementScope oMs = new System.Management.ManagementScope("\\\\localhost", oConn);
System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select AddressWidth from Win32_Processor");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
string addressWidth = null;
foreach (ManagementObject oReturn in oReturnCollection)
{
addressWidth = oReturn["AddressWidth"].ToString();
}
return addressWidth;
}
3 文件系統的轉向
32位進程不能加載64位Dll,64位進程也不可以加載32位Dll。Windows的系統目錄包含了所有安裝的應用程序和它們的Dll文件,根據我們所述的規則,它應該被分爲給64位應用程序的目錄和給32位應用程序的目錄。如果不這樣,我們就無法區分32位和64位的Dll文件。對於64位應用程序,其文件通常被放在%windir%\system32和%programfiles%(比如:c:\program files)。對於32位應用程序,其文件通常在%windir%\syswow64和C:\program files (x86)下面。如果我們用32位程序去訪問%windir%\system32,不管我們用硬編碼還是其它的方式,系統都會自動地給我們轉向到%windir%\syswow64下面。這種轉向對於每個32位應用程序默認都是打開的。但是這種轉向對於我們來說並不總是需要的。那麼我們可以在C#裏面調用相關的API來關閉和打開這種轉向。常用的函數有3個,Wow64DisableWow64FsRedirection(關閉系統轉向),Wow64RevertWow64FsRedirection(打開系統轉向),Wow64EnableWow64FsRedirection(打開系統轉向)。但是Wow64EnableWow64FsRedirection在嵌套使用的時候不可靠,所以通常用上面的Wow64RevertWow64FsRedirection來打開文件系統轉向功能。在C#中,我們可以利用DllImport直接調用這兩個函數。但是要注意到的是,在32位的Kernel.dll中是沒有這兩個函數的。那麼在C++中應該使用LoadLibrary來動態加載這兩個函數。否則會因爲找不到這兩個函數而無法通過編譯。而且在目前的使用中,發現這兩個函數有一個小小的問題。如果我們在調用了Wow64DisableWow64FsRedirection後去調用Comdlg32.dll的GetOpenFileName函數,是無法調用成功的。但是也得不到Error的值。在C#中可以用如下的代碼關閉和打開文件的轉向。
聲明調用規則
public static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
[DllImport( "Kernel32.dll", CharSet=CharSet.Auto, SetLastError = true)]
public static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr);
關閉轉向
打開轉向
要注意的是,關閉和打開要成對出現。以免出現混亂的行爲。
4 註冊表的轉向
爲了防止註冊表鍵衝突,註冊表在某些鍵也分成了兩個部分。一部分是專門給64位系統訪問的,另一部分是專門給32位系統訪問的,放在Wow6432Node下面。當32位程序去訪問某些鍵值的時候,和文件轉向類似,系統也會自動地把程序的訪問轉向到Wow6432Node下面。Wow6432Node這個節點存在於HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER下面。如果我們希望關閉這個轉向的話,可以通過上面的Wow64DisableWow64FsRedirection和RegOpenKeyEx方法辦到。RegOpenEx方法在C#中調用聲明方法如下:
public static extern uint RegOpenKeyEx( UIntPtr hKey,string lpSubKey, uint ulOptions,int samDesired,out IntPtr phkResult);
其中需要注意的是samDesired這個參數。這個參數可以取 KEY_ALL_ACCESS, KEY_QUERY_VALUE, KEY_WOW64_64KEY等值(詳情可以查閱MSDN)。當我們已經關閉了文件系統的轉向,那麼就可以利用:(KEY_QUERY_VALUE | KEY_WOW64_64KEY)來得到註冊表的完全訪問權限。這個地方需要注意的是,在Vista下面,有一些註冊表項是隻讀的,如果用了KEY_ALL_ACCESS這個參數,就會出現“Access is denied” 這個錯誤(ErrorCode = 5)。因此,如果不是要寫入註冊表的話,最好不要使用KEY_ALL_ACCESS。我們可以用如下代碼來完全訪問註冊表。
private static UIntPtr TransferKeyName(string keyName)
{
UIntPtr HKEY_CLASSES_ROOT = (UIntPtr)0x80000000;
UIntPtr HKEY_CURRENT_USER = (UIntPtr)0x80000001;
UIntPtr HKEY_LOCAL_MACHINE = (UIntPtr)0x80000002;
UIntPtr HKEY_USERS = (UIntPtr)0x80000003;
UIntPtr HKEY_CURRENT_CONFIG = (UIntPtr)0x80000005;
switch(keyName)
{
case "HKEY_CLASSES_ROOT":
return HKEY_CLASSES_ROOT;
case "HKEY_CURRENT_USER":
return HKEY_CURRENT_USER;
case "HKEY_LOCAL_MACHINE":
return HKEY_LOCAL_MACHINE;
case "HKEY_USERS":
return HKEY_USERS;
case "HKEY_CURRENT_CONFIG":
return HKEY_CURRENT_CONFIG;
}
return HKEY_CLASSES_ROOT;
}
public static bool OpenRegKey(string keyName, string subKeyName)
{
UIntPtr hKey = TransferKeyName(keyName);
UIntPtr phkResult = UIntPtr.Zero;
int KEY_QUERY_VALUE = (0x0001);
int KEY_WOW64_64KEY = (0x0100);
int KEY_ALL_WOW64 = (KEY_QUERY_VALUE | KEY_WOW64_64KEY);
if(Is64bitOS && !IsWow64RedirectionEnabled) //The os is 64 bit and the FileRedirection is closed
{
samDesired = KEY_ALL_WOW64;
}
if(RegOpenKeyEx(hKey, subKeyName,0,samDesired,out phkResult) == 0) //ERROR_SUCCESS=0
{
return true;
}
int errCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
return false;
}
5 進一步閱讀
需要更多的64位編程的資料,可以查閱下面的鏈接
http://www.microsoft.com/whdc/system/platform/64bit/WoW64_bestprac.mspx
http://msdn2.microsoft.com/en-us/library/aa384187.aspx
http://msdn2.microsoft.com/en-us/library/aa384235.aspx
*【Author】:flyingbread
*【Date】:2007年1月21日
*【Notice】:
出處:http://tech.it168.com/KnowledgeBase/Articles/7/4/a/74a6990cf7ded7797cf258b60a59aac1.htm