从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.Registry64
与RegistryView.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)?.GetValueNames(); } public static IEnumerable<string> GetAllRegValueNames(string RegPath, RegistryHive hive = RegistryHive.LocalMachine) { var reg64 = GetRegValueNames(RegistryView.Registry64, RegPath, hive); var reg32 = GetRegValueNames(RegistryView.Registry32, 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)?.GetValue(ValueName); } public static object GetRegValue(string RegPath, string ValueName, RegistryHive hive = RegistryHive.LocalMachine) { return GetRegValue(RegistryView.Registry64, RegPath, ValueName, hive) ?? GetRegValue(RegistryView.Registry32, 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); } }