C# 从32位应用程序读取64位注册表
我有一个c#单元测试项目,它是为任何CPU编译的。我们的构建服务器是一台64位计算机,并安装了一个64位SQL Express实例 测试项目使用与以下类似的代码来标识.MDF文件的路径:C# 从32位应用程序读取64位注册表,c#,.net,64-bit,registry,32-bit,C#,.net,64 Bit,Registry,32 Bit,我有一个c#单元测试项目,它是为任何CPU编译的。我们的构建服务器是一台64位计算机,并安装了一个64位SQL Express实例 测试项目使用与以下类似的代码来标识.MDF文件的路径: private string GetExpressPath() { RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance N
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位工作站上运行良好,在构建服务器上运行正常,直到最近我使用NCover启用了代码覆盖率分析。因为NCover使用32位COM组件,所以测试运行程序(Gallio)作为32位进程运行
检查注册表时,下没有“实例名称”键
HKEY\U LOCAL\U MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server
在32位模式下运行的应用程序是否有办法访问Wow6432Node外部的注册表?在创建/打开注册表项时,必须使用KEY\u WOW64\u 64KEY参数。但是这在注册表类中是不可能的,只有在直接使用API时才可能 可能有助于您入门。尝试以下方法(从32位进程):
(发现)。读取64位注册表是可能的,因为它是一个Windows子系统,提供从32位应用程序中访问64位的权限。(类似地,在旧的基于NT的Windows版本中,它被称为32位Windows中的一个仿真层,用于支持16位应用程序) 在64位Windows下使用.NET Framework 4.x仍有对注册表访问的本机支持。以下代码在Windows 7、64位以及Windows 10、64位上进行了测试 您可以执行以下操作,而不是使用
“Wow6432Node”
,后者通过将一个注册表树映射到另一个注册表树来模拟节点,使其虚拟地出现在那里:
决定是需要访问64位还是32位注册表,并按如下所述使用它。您还可以使用我在后面提到的代码(附加信息部分),它创建一个联合查询,以便在一个查询中从两个节点获取注册表项,因此您仍然可以使用它们的实际路径来查询它们
64位注册表
要访问64位注册表,可以按如下方式使用RegistryView.Registry64
:
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();
localKey.Close();
}
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64));
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();
localKey32.Close();
}
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32));
32位注册表
如果要访问32位注册表,请按如下方式使用RegistryView.Registry32
:
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();
localKey.Close();
}
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64));
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();
localKey32.Close();
}
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32));
不要混淆,这两个版本都使用Microsoft.Win32.RegistryHive.LocalMachine
作为第一个参数,您可以通过第二个参数(RegistryView.Registry64
与RegistryView.Registry32
)来区分是使用64位还是32位
注意
- 在64位Windows上,
包含在64位系统上运行的32位应用程序使用的值。只有真正的64位应用程序将其值直接存储在HKEY\U LOCAL\U MACHINE\Software\Wow6432Node
中。子树HKEY\U LOCAL\U MACHINE\Software
对于32位应用程序是完全透明的,32位应用程序仍然可以像预期的那样查看Wow6432Node
(这是一种重定向)。在旧版本的Windows以及32位Windows 7(和Vista 32位)中,子树HKEY\u LOCAL\u MACHINE\Software
显然不存在Wow6432Node
- 由于Windows 7(64位)中的错误,32位源代码版本始终返回“Microsoft”,而不管您注册了哪个组织,而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");
其他信息-供实际使用: 我想在评论中添加一个有趣的方法,我用他的方法开发了一些有用的函数:在某些情况下,你想取回所有的键,不管它是32位还是64位。SQL实例名称就是这样一个例子。在这种情况下,可以使用联合查询,如下所示(C#6或更高): 将为您提供一个值名称和sqlRegPath中的值的列表 注意:如果在上面相应的函数中省略
ValueName
参数,则可以访问键的默认值(由命令行工具REGEDT32.EXE
显示为(默认值)
)
要获取注册表项中的子项列表,请使用函数GetRegKeyNames
或GetAllRegKeyNames
。您可以使用此列表遍历注册表中的其他项
示例2:获取已安装软件的卸载信息
var currentVersionRegPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $@"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);
将获得所有32位和64位卸载密钥
请注意函数中需要的空处理,因为SQL server可以安装为32位或64位(上面的示例1)。函数是重载的,因此如果需要,您仍然可以传递32位或64位参数-但是,如果您忽略它,它将尝试读取64位,如果失败(空值),它将读取32位值
这里有一个特点:因为GetAllRegValueNames
通常用于循环上下文(参见上面的示例1),它返回一个空的枚举值,而不是null
,以简化foreach
循环:如果不这样处理,循环必须以检查null
的if
语句作为前缀,这样做会很麻烦,因此在函数中只处理一次
为什么要为null而烦恼?因为如果你不在乎,你会有更多的麻烦来找出为什么在代码中抛出null引用异常-你会花很多时间找出它发生的位置和原因。如果它发生在生产中,您将忙于研究日志文件或事件日志(我希望您已经实现了日志记录)。。。是
var currentVersionRegPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $@"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);
static string[] test { get { return null;} } // property used to return null
static void Main()
{
test.Dump(); // output: null
// "elvis" operator:
test?.Dump(); // output:
// "elvis" operator for arrays
test?[0].Dump(); // output:
(test?[0]).Dump(); // output: null
// combined with null coalescing operator (brackets required):
(test?[0]??"<null>").Dump(); // output: "<null>"
}
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);
}
}
string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
var list = key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName).GetValue("DisplayName")).ToList();
key.Close();
}
registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);