我的上一篇文章已經闡述了“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位程式所通路的系統資料庫位置)。