.NET 3.5中替代RegistryKey.OpenBaseKey的一些方法有哪些?

9
我一直在开发一个安装程序包,并使用RegistryKey.OpenBaseKey与自定义操作一起工作,在MSI包中从64位或32位注册表中打开、添加/删除键,但这需要我在运行我的安装程序之前在目标计算机上安装.NET Framework 4,使用引导程序或其他方式,因为OpenBaseKey只在.NET Framework 4中引入。理想情况下,我想针对.NET Framework 3.5,仍然能够修改64位或32位注册表,就像OpenBaseKey一样;那么我就不需要.NET 4和它的安装开销。
对于那些不想将.NET 4作为前提条件的人,是否有替代OpenBaseKey的方法?也许像P/Invoking某个WinAPI方法来启动这个过程?我不确定需要什么。
3个回答

19

对于那些希望使用C#解决以前版本的.NET,而不必重构太多代码的人,这并不完美,但可以使用反射实现。我在 XSharper源代码 中发现了这个技巧。

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);

@BlackFrog 很高兴能帮助到我的同行开发人员 :) - Alexandru
非常棒的代码!感谢您的帮助。然而,代码和注释之间似乎存在不匹配之处。如果它确实是针对“Windows 2000及更高版本”,我认为比较应该是“Environment.OSVersion.Version.Major >= 5”。 - James R
@JamesR 哎呀,我修改了异常为“平台或操作系统必须是Windows XP或更高版本。”不过无所谓了,如果你正在编写Windows 2000的代码,我认为你有更大的问题哈哈...通常我不会为任何已被制造商停止支持的操作系统编写代码。 - Alexandru
这段代码需要以管理员身份运行,你能告诉我为什么吗?或者有没有绕过“访问被拒绝”的方法?@Alexandru - undefined

2
对于低于4.0版本的.NET,没有框架API允许访问替代注册表视图。为了访问替代视图,您必须调用本地API RegOpenKeyEx 并传递相应的KEY_WOW64_32KEYKEY_WOW64_64KEY标志。

通常的做法是使用C++/CLI混合模式程序集,或者使用P/Invoke。然而,这并不是很有趣。注册表API是一些比较棘手的API,因为它们支持多种值类型。

2

需要定位的注册表:

RegistryKey localMachine = Registry.LocalMachine; //For example

获取所需的值:

RegistryKey lKey = localMachine.OpenSubKey(@"SOFTWARE\...\", false);

您的回答如果能更详细地解释如何适用于问题,将会更有帮助。 - Rob Goodwin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接