2014-10-06 4 views
8

Я работал над пакетом установщика и использовал RegistryKey.OpenBaseKey для работы с пользовательскими действиями, которые открывают и добавляют/удаляют ключи из 64-разрядного реестра или 32-разрядного реестра из пакета MSI, но это требует от меня установить .NET Framework 4 на целевой машине перед запуском моего установщика, используя загрузчик или что-то еще, потому что OpenBaseKey был представлен только в .NET Framework 4. В идеале я хотел бы настроить таргетинг только на .NET Framework 3.5 и по-прежнему способный модифицировать либо 64-битные, либо 32-разрядные кусты реестра, как в OpenBaseKey; то мне не понадобится .NET 4 и накладные расходы на ее установку.Каковы некоторые альтернативы RegistryKey.OpenBaseKey в .NET 3.5?

Есть ли альтернативы OpenBaseKey для тех из нас, кто не хотел бы делать .NET 4 необходимым условием? Что-то вроде P/Вызов определенного метода WinAPI, чтобы это выгнать, возможно? Я не уверен, что это займет.

ответ

13

Для тех, кто заинтересован в решении C# для некоторых из предыдущих версий .NET, чтобы не перерабатывать слишком много кода, его не очень красиво, но здесь оно полностью выполнимо с использованием отражения. Я нашел этот трюк в 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

Это здорово. Хотел бы я дать вам больше очков. –

+0

@BlackFrog Рад, что это помогает моим коллегам-разработчикам :) – Alexandru

+0

Удивительный код! Спасибо за помощь. Однако похоже, что существует несовпадение кода и комментариев. Если это действительно так для Windows 2000 и более поздних версий, я думаю, что сравнение должно быть «Environment.OSVersion.Version.Major> = 5» –

2

Для версий .NET, предшествующих версии 4, нет API-интерфейса фреймворка, который обеспечивает доступ к альтернативным представлениям реестра. Чтобы получить доступ к альтернативным представлениям, вы должны вызвать собственный API RegOpenKeyEx, который передает соответствующий флажок KEY_WOW64_32KEY или KEY_WOW64_64KEY.

Общие способы сделать это с помощью сборщиков смешанного режима C++/CLI или с использованием P/Invoke. Однако это не очень весело. API-интерфейсы реестра являются одними из самых неудобных в использовании, поскольку они поддерживают несколько типов данных для значений.

Смежные вопросы