2014-10-06 52 views
8

我一直在使用安裝程序包,並使用RegistryKey.OpenBaseKey來處理自定義操作,這些操作可以從64位註冊表或32位註冊表打開或向MSI軟件包中添加/刪除鍵,但這需要我在運行我的安裝程序之前,使用引導程序或其他內容在目標計算機上安裝.NET Framework 4,因爲OpenBaseKey僅在.NET Framework 4中引入。理想情況下,我只想定位到.NET Framework 3.5並仍然是能夠修改64位或32位註冊表配置單元,如OpenBaseKey;那麼我不需要.NET 4和安裝它的開銷。.NET 3.5中的RegistryKey.OpenBaseKey有哪些替代方法?

是否有替代OpenBaseKey對於我們這些誰不喜歡讓.NET 4的先決條件?或許像P /調用某種WinAPI方法來解決這個問題?我不確定會發生什麼。

回答

13

對於任何對.NET的某些早期版本的C#解決方案感興趣的人,爲了不必重構太多的代碼,它並不漂亮,但在這裏它完全可以使用反射。我在XSharper source code中發現了這個技巧。

public static class RegistryExtensions 
{ 

    public enum RegistryHiveType 
    { 
     X86, 
     X64 
    } 

    static Dictionary<RegistryHive, UIntPtr> _hiveKeys = new Dictionary<RegistryHive, UIntPtr> { 
     { RegistryHive.ClassesRoot, new UIntPtr(0x80000000u) }, 
     { RegistryHive.CurrentConfig, new UIntPtr(0x80000005u) }, 
     { RegistryHive.CurrentUser, new UIntPtr(0x80000001u) }, 
     { RegistryHive.DynData, new UIntPtr(0x80000006u) }, 
     { RegistryHive.LocalMachine, new UIntPtr(0x80000002u) }, 
     { RegistryHive.PerformanceData, new UIntPtr(0x80000004u) }, 
     { RegistryHive.Users, new UIntPtr(0x80000003u) } 
    }; 

    static Dictionary<RegistryHiveType, RegistryAccessMask> _accessMasks = new Dictionary<RegistryHiveType, RegistryAccessMask> { 
     { RegistryHiveType.X64, RegistryAccessMask.Wow6464 }, 
     { RegistryHiveType.X86, RegistryAccessMask.WoW6432 } 
    }; 

    [Flags] 
    public enum RegistryAccessMask 
    { 
     QueryValue   = 0x0001, 
     SetValue   = 0x0002, 
     CreateSubKey  = 0x0004, 
     EnumerateSubKeys = 0x0008, 
     Notify    = 0x0010, 
     CreateLink   = 0x0020, 
     WoW6432    = 0x0200, 
     Wow6464    = 0x0100, 
     Write    = 0x20006, 
     Read    = 0x20019, 
     Execute    = 0x20019, 
     AllAccess   = 0xF003F 
    } 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto)] 
    public static extern int RegOpenKeyEx(
     UIntPtr hKey, 
     string subKey, 
     uint ulOptions, 
     uint samDesired, 
     out IntPtr hkResult); 

    public static RegistryKey OpenBaseKey(RegistryHive registryHive, RegistryHiveType registryType) 
    { 
     UIntPtr hiveKey = _hiveKeys[registryHive]; 
     if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major > 5) 
     { 
      RegistryAccessMask flags = RegistryAccessMask.QueryValue | RegistryAccessMask.EnumerateSubKeys | RegistryAccessMask.SetValue | RegistryAccessMask.CreateSubKey | _accessMasks[registryType]; 
      IntPtr keyHandlePointer = IntPtr.Zero; 
      int result = RegOpenKeyEx(hiveKey, String.Empty, 0, (uint)flags, out keyHandlePointer); 
      if (result == 0) 
      { 
       var safeRegistryHandleType = typeof(SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle"); 
       var safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET < 4 
       if (safeRegistryHandleConstructor == null) 
        safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET >= 4 
       var keyHandle = safeRegistryHandleConstructor.Invoke(new object[] { keyHandlePointer, true }); 
       var net3Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { safeRegistryHandleType, typeof(bool) }, null); 
       var net4Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }, null); 
       object key; 
       if (net4Constructor != null) 
        key = net4Constructor.Invoke(new object[] { keyHandlePointer, true, false, false, hiveKey == _hiveKeys[RegistryHive.PerformanceData] }); 
       else if (net3Constructor != null) 
        key = net3Constructor.Invoke(new object[] { keyHandle, true }); 
       else 
       { 
        var keyFromHandleMethod = typeof(RegistryKey).GetMethod("FromHandle", BindingFlags.Static | BindingFlags.Public, null, new[] { safeRegistryHandleType }, null); 
        key = keyFromHandleMethod.Invoke(null, new object[] { keyHandle }); 
       } 
       var field = typeof(RegistryKey).GetField("keyName", BindingFlags.Instance | BindingFlags.NonPublic); 
       if (field != null) 
        field.SetValue(key, String.Empty); 
       return (RegistryKey)key; 
      } 
      else if (result == 2) // The key does not exist. 
       return null; 
      throw new Win32Exception(result); 
     } 
     throw new PlatformNotSupportedException("The platform or operating system must be Windows XP or later."); 
    } 
} 

實例:

var key64 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X64); 
var key32 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X86); 
+1

這是很大的。我希望我能給你更多的觀點。 – 2016-02-18 16:49:29

+0

@BlackFrog很高興它幫助我的同伴開發者:) – Alexandru 2016-02-19 21:18:27

+0

真棒代碼!謝謝您的幫助。但是看起來代碼和評論之間存在不匹配。如果真的是「Windows 2000及更高版本」,我認爲比較應該是「Environment.OSVersion.Version.Major> = 5」 – 2017-04-06 13:05:19

2

對於早於版本4的.NET版本,沒有允許訪問備用註冊表視圖的框架API。爲了訪問備用視圖,必須根據需要調用本地API RegOpenKeyEx傳遞KEY_WOW64_32KEYKEY_WOW64_64KEY標誌之一。

常見的方法是使用C++/CLI混合模式程序集或使用P/Invoke。然而,這並不是很有趣。註冊表API是一些比較尷尬的用法,因爲它們支持多種數據類型的值。

相關問題