2012年7月2日 星期一

C# 32位程序訪問64位系統註冊表


  我的上一篇文章已經闡述了「32位程序和64位程序在64位平台上讀\寫註冊表的區別」,那麼接下來將要回答上篇所留下來的一個問題:32位程序如何訪問64位系統註冊表(即:64位程序所訪問的註冊表位置)。
  我們已經知道:
    ①:本機模式 64 位程序運行在純模式下,並且訪問鍵和存儲在以下註冊表子鍵中的值:HKEY_LOCAL_MACHINE\Software
    ②:32 位程序運行在 WOW64 模式下,並且訪問鍵和值存儲在以下註冊表子項中:HKEY_LOCAL_MACHINE\Software\WOW6432nod
  那麼要實現32為程序訪問64位註冊表信息,還要知道如下概念:1:文件系統轉向。2:註冊表重定向(轉向)。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 位和 64 位 COM 註冊和程序共存狀態,WOW64 子系統提供 32 位程序使用的註冊表的另一個視圖。在 WOW64 子系統使用註冊表
    重定向截獲位級別的註冊表調用。註冊表重定向還可以確保註冊表調用被定向到在註冊表中正確的分支。 
    當我們安裝新程序或 Windows x64 版的計算機上運行程序時,所做的 64 位程序的註冊表調用訪問 HKEY_LOCAL_MACHINE\Software 註冊表子鍵
    不重定向。WOW64 截獲由 32 位程序的註冊表調用到 HKEY_LOCAL_MACHINE\Software,然後將它們重定向到
    HKEY_LOCAL_MACHINE\Software\WOW6432node 子鍵。 通過重定向僅 32 位程序調用,WOW64 可確保程序始終寫入相應的註冊表子鍵。
    註冊表重定向不要求程序代碼修改,和此過程是對用戶透明。
    ③:註冊表反射
    反射使兩個相同的註冊表,以支持同時進行的本機和 WOW64 操作的物理副本的存在,
    打開註冊表的 64 位節在所有時間和註冊表反射提供了一種容納 32 位的實時方法。

  簡單的瞭解了這些,下面說一下具體的實現步驟:
    關閉64位(文件系統)的操作轉向
      獲得操作Key值的句柄
        關閉註冊表轉向(禁止特定項的註冊表反射)
      獲取訪問的Key值
        打開註冊表轉向(開啟特定項的註冊表反射)
    開啟64位(文件系統)的操作轉向

  【註:由於我們在程序中用了DllImport,所以要引入命名空間:System.Runtime.InteropServices】
  下面請看代碼示例
複製代碼
1 using System; 2  using System.Collections.Generic; 3  using System.Linq; 4  using System.Text; 5  using Microsoft.Win32; 6  using System.Runtime.InteropServices; 7 8  namespace OperateRegistrationTable 9 { 10 class Programe 11 { 12 static void Main(string[] args) 13 { 14 string myParentKeyName = "HKEY_LOCAL_MACHINE"; 15 string mySubKeyName = @"SOFTWARE\EricSun\MyTestKey"; 16 string myKeyName = "MyKeyName"; 17 18 string value = string.Empty; 19 value = Utility.Get64BitRegistryKey(myParentKeyName, mySubKeyName, myKeyName); 20 Console.WriteLine("The Value is: {0}", value); 21 } 22 } 23 24 public class Utility 25 { 26 #region 32位程序讀寫64註冊表 27 28 static UIntPtr HKEY_CLASSES_ROOT = (UIntPtr)0x80000000; 29 static UIntPtr HKEY_CURRENT_USER = (UIntPtr)0x80000001; 30 static UIntPtr HKEY_LOCAL_MACHINE = (UIntPtr)0x80000002; 31 static UIntPtr HKEY_USERS = (UIntPtr)0x80000003; 32 static UIntPtr HKEY_CURRENT_CONFIG = (UIntPtr)0x80000005; 33 34 // 關閉64位(文件系統)的操作轉向 35   [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 36 public static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr); 37 // 開啟64位(文件系統)的操作轉向 38   [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 39 public static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr); 40 41 // 獲取操作Key值句柄 42   [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 43 public static extern uint RegOpenKeyEx(UIntPtr hKey, string lpSubKey, uint ulOptions, 
                                  int samDesired, out IntPtr phkResult); 44 //關閉註冊表轉向(禁用特定項的註冊表反射) 45 [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 46 public static extern long RegDisableReflectionKey(IntPtr hKey); 47 //使能註冊表轉向(開啟特定項的註冊表反射) 48 [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 49 public static extern long RegEnableReflectionKey(IntPtr hKey); 50 //獲取Key值(即:Key值句柄所標誌的Key對象的值) 51 [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 52 private static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, 53 out uint lpType, System.Text.StringBuilder lpData, 54 ref uint lpcbData); 55 56 private static UIntPtr TransferKeyName(string keyName) 57 { 58 switch (keyName) 59 { 60 case "HKEY_CLASSES_ROOT": 61 return HKEY_CLASSES_ROOT; 62 case "HKEY_CURRENT_USER": 63 return HKEY_CURRENT_USER; 64 case "HKEY_LOCAL_MACHINE": 65 return HKEY_LOCAL_MACHINE; 66 case "HKEY_USERS": 67 return HKEY_USERS; 68 case "HKEY_CURRENT_CONFIG": 69 return HKEY_CURRENT_CONFIG; 70 } 71 72 return HKEY_CLASSES_ROOT; 73 } 74 75 public static string Get64BitRegistryKey(string parentKeyName, string subKeyName, string keyName) 76 { 77 int KEY_QUERY_VALUE = (0x0001); 78 int KEY_WOW64_64KEY = (0x0100); 79 int KEY_ALL_WOW64 = (KEY_QUERY_VALUE | KEY_WOW64_64KEY); 80 81 try 82 { 83 //將Windows註冊表主鍵名轉化成為不帶正負號的整形句柄(與平台是32或者64位有關) 84 UIntPtr hKey = TransferKeyName(parentKeyName); 85 86 //聲明將要獲取Key值的句柄 87 IntPtr pHKey = IntPtr.Zero; 88 89 //記錄讀取到的Key值 90 StringBuilder result = new StringBuilder("".PadLeft(1024)); 91 uint resultSize = 1024; 92 uint lpType = 0; 93 94 //關閉文件系統轉向 95 IntPtr oldWOW64State = new IntPtr(); 96 if (Wow64DisableWow64FsRedirection(ref oldWOW64State)) 97 { 98 //獲得操作Key值的句柄 99 RegOpenKeyEx(hKey, subKeyName, 0, KEY_ALL_WOW64, out pHKey);100 101 //關閉註冊表轉向(禁止特定項的註冊表反射) 102 RegDisableReflectionKey(pHKey);103 104 //獲取訪問的Key值 105 RegQueryValueEx(pHKey, keyName, 0, out lpType, result, ref resultSize);106 107 //打開註冊表轉向(開啟特定項的註冊表反射) 108 RegEnableReflectionKey(pHKey);109 }110 111 //打開文件系統轉向 112 Wow64RevertWow64FsRedirection(oldWOW64State);113 114 //返回Key值 115 return result.ToString().Trim();116 }117 catch (Exception ex)118 {119 return null;120 }121 }122 123 #endregion 124 }125 }
複製代碼
Get64BitRegistryKey函數的三個參數分別代表:主鍵名(如:HKEY_LOCAL_MACHINE等),子鍵名,Key名,返回的是Key的Value(64位系統註冊表的鍵值),通過上面的方法就完全可以實現用32程序訪問64位系統註冊表(即:64位程序所訪問的註冊表位置)。

沒有留言:

張貼留言