从32位应用程序读取64位registry

我有为AnyCPU编译的ac#unit testing项目。 我们的构build服务器是一个64位的机器,并安装了一个64位的SQL Express实例。

testing项目使用类似于以下内容的代码来标识.MDF文件的path:

private string GetExpressPath() { RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" ); string sqlExpressKeyName = (string) sqlServerKey.GetValue( "SQLEXPRESS" ); RegistryKey sqlInstanceSetupKey = sqlServerKey.OpenSubKey( sqlExpressKeyName + @"\Setup" ); return sqlInstanceSetupKey.GetValue( "SQLDataRoot" ).ToString(); } 

这段代码在我们的32位工作站上工作正常,并且在构build服务器上工作正常,直到我最近使用NCover启用了代码覆盖率分析。 由于NCover使用32位COM组件,testing运行器(Gallio)以32位进程运行。

检查registry,下面没有“实例名称”键

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

有没有办法在32位模式下运行的应用程序访问Wow6432Node之外的registry?

创build/打开registry项时必须使用KEY_WOW64_64KEY参数。 但是AFAIK对于registry类来说是不可能的,但是只能直接使用API​​。

这可能有助于你开始。

在使用.NET Framework 4.x的 64位Windows下,仍然有对registry访问的本机支持。 以下代码是在Windows 7,64位Windows 10,64位下testing的 。 要访问64位registry ,您可以使用:

 string value64 = string.Empty; RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); localKey = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); if (localKey != null) { value64 = localKey.GetValue("RegisteredOrganization").ToString(); } Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64)); 

如果要访问32位registry ,请使用:

 string value32 = string.Empty; RegistryKey localKey32 = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry32); localKey32 = localKey32.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); if (localKey32 != null) { value32 = localKey32.GetValue("RegisteredOrganization").ToString(); } Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32)); 

不要混淆,两个版本都使用Microsoft.Win32.RegistryHive.LocalMachine作为第一个参数,您可以通过第二个参数RegistryView.Registry64RegistryView.Registry32 )来区分是使用64位还是32位

请注意

  • 在64位Windows上, HKEY_LOCAL_MACHINE\Software\Wow6432Node包含在64位系统上运行的32位应用程序使用的值。 只有真正的64位应用程序直接将其值存储在HKEY_LOCAL_MACHINE\Software 。 子树Wow6432Node对于32位应用程序是完全透明的,32位应用程序仍然可以看到HKEY_LOCAL_MACHINE\Software因为他们期望它(这是一种redirect)。 在较旧版本的Windows以及32位Windows 7(和Vista 32位)中,子树Wow6432Node显然不存在。

  • 由于Windows 7(64位)中的错误,32位源代码版本始终返回“Microsoft”,无论您注册了哪个组织,而64位源代码版本将返回正确的组织。

回到您提供的示例,按照以下方式访问64位分支:

 RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); RegistryKey sqlServerKey = localKey.OpenSubKey( @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"); string sqlExpressKeyName = (string) sqlServerKey.GetValue("SQLEXPRESS"); 

更新:

我想添加一个有趣的方法Johny Skovdal在评论中提出的build议,我已经select使用他的方法来开发一些有用的function:在某些情况下,无论是32位还是32位, 64位。 SQL实例名称就是这样一个例子。 在这种情况下,您可以使用联合查询(C#6或更高版本):

 public static IEnumerable<string> GetRegValueNames(RegistryView view, string regPath, RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.G‌​etValueNames(); } public static IEnumerable<string> GetAllRegValueNames(string RegPath, RegistryHive hive = RegistryHive.LocalMachine) { var reg64 = GetRegValueNames(RegistryView.Registry64, RegPath, hive); var reg32 = GetRegValueNames(RegistryView.Re‌​gistry32, RegPath, hive); var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32); return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x); } public static object GetRegValue(RegistryView view, string regPath, string ValueName, RegistryHive hive = RegistryHive.LocalMachine) { return RegistryKey.OpenBaseKey(hive, view) ?.OpenSubKey(regPath)?.G‌​etValue(ValueName); } public static object GetRegValue(string RegPath, string ValueName, RegistryHive hive = RegistryHive.LocalMachine) { return GetRegValue(RegistryView.Registry64, RegPath, ValueName, hive) ?? GetRegValue(RegistryView.Re‌​gistry32, RegPath, ValueName, hive); } 

现在你可以简单地使用上面的函数,如下所示:

 var sqlRegPath=@"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"; foreach (var valueName in GetAllRegValueNames(sqlRegPath)) { var value=GetRegValue(sqlRegPath, valueName); Console.WriteLine($"{valueName}={value}"); } 

这将给你一个在sqlRegPath中的值名称和值的列表。

请注意函数中所需的空处理,因为SQL Server可以安装为32位或64位。 函数是重载的,所以你仍然可以传递32位或64位参数(如果需要的话) – 但是,如果你忽略它,那么它将尝试读取64位,如果失败(空值),它读取32位值。


提示:您可以使用Linqpad在Windows 7下testing所有示例。不需要安装。 不要忘记按F4并在名称空间导入选项卡中inputMicrosoft.Win32 。 在Visual Studio中,您需要using Microsoft.Win32; 在你的代码的顶部。

我没有足够的代表评论,但值得指出的是,它使用OpenRemoteBaseKey打开远程registry时,它的工作原理。 添加RegistryView.Registry64参数允许计算机A上的32位程序访问计算机B上的64位registry。在传递该参数之前,我的程序正在读取OpenRemoteBaseKey之后的32位,并且未find密钥I之后。

注意:在我的testing中,远程机器实际上是我的机器,但是我通过OpenRemoteBaseKey访问它,就像我为不同的机器一样。

试试这个(从一个32位进程):

 > %WINDIR%\sysnative\reg.exe query ... 

(在这里find)。

如果您不能使用.NET 4及其RegistryKey.OpenBaseKey(..., RegistryView.Registry64) ,则需要直接使用Windows API。

最小的interop就像:

 internal enum RegistryFlags { ... RegSz = 0x02, ... SubKeyWow6464Key = 0x00010000, ... } internal enum RegistryType { RegNone = 0, ... } [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int RegGetValue( UIntPtr hkey, string lpSubKey, string lpValue, RegistryFlags dwFlags, out RegistryType pdwType, IntPtr pvData, ref uint pcbData); 

像这样使用它:

 IntPtr data = IntPtr.Zero; RegistryType type; uint len = 0; RegistryFlags flags = RegistryFlags.RegSz | RegistryFlags.SubKeyWow6464Key; UIntPtr key = (UIntPtr)((uint)RegistryHive.LocalMachine); const string subkey= @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"; const string value = "SQLEXPRESS"; if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0) { data = Marshal.AllocHGlobal((int)len); if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0) { string sqlExpressKeyName = Marshal.PtrToStringUni(data); } }