C# 从32位应用程序读取64位注册表

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

我有一个c#单元测试项目,它是为任何CPU编译的。我们的构建服务器是一台64位计算机,并安装了一个64位SQL Express实例

测试项目使用与以下类似的代码来标识.MDF文件的路径:

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上,
    HKEY\U LOCAL\U MACHINE\Software\Wow6432Node
    包含在64位系统上运行的32位应用程序使用的值。只有真正的64位应用程序将其值直接存储在
    HKEY\U LOCAL\U MACHINE\Software
    中。子树
    Wow6432Node
    对于32位应用程序是完全透明的,32位应用程序仍然可以像预期的那样查看
    HKEY\u LOCAL\u MACHINE\Software
    (这是一种重定向)。在旧版本的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");

其他信息-供实际使用: 我想在评论中添加一个有趣的方法,我用他的方法开发了一些有用的函数:在某些情况下,你想取回所有的键,不管它是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);