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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/974038/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:37:07  来源:igfitidea点击:

Reading 64bit Registry from a 32bit application

c#.net64-bitregistry32-bit

提问by David Gardiner

I have a c# unit test project that is compiled for AnyCPU. Our build server is a 64bit machine, and has a 64bit SQL Express instance installed.

我有为 AnyCPU 编译的 ac# 单元测试项目。我们的构建服务器是 64 位机器,并安装了 64 位 SQL Express 实例。

The test project uses code similar to the following to identify the path to the .MDF files:

测试项目使用类似于以下的代码来标识 .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();
    }

This code works fine on our 32bit workstations, and did work ok on the build server until I recently enabled code coverage analysis with NCover. Because NCover uses a 32bit COM component, the test runner (Gallio) runs as a 32bit process.

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

Checking the registry, there is no "Instance Names" key under

检查注册表,下面没有“Instance Names”项

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server

Is there a way for an application running in 32bit mode to access the registry outside Wow6432Node?

有没有办法让以 32 位模式运行的应用程序访问 Wow6432Node 之外的注册表?

采纳答案by Stefan

you have to use the KEY_WOW64_64KEY param when creating/opening the registry key. But AFAIK that's not possible with the Registry class but only when using the API directly.

创建/打开注册表项时,您必须使用 KEY_WOW64_64KEY 参数。但是 AFAIK 在 Registry 类中是不可能的,而只有在直接使用 API 时才可能。

Thismight help to get you started.

可能有助于您入门。

回答by akira

try this (from a 32bit process):

试试这个(来自 32 位进程):

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

(found that here).

(在这里找到)。

回答by Matt

There is still native support for registry access under 64 bit Windows using .NET Framework 4.x. The following code is tested with  Windows 7, 64 bit  and also with  Windows 10, 64 bit.

在使用.NET Framework 4.x 的64 位 Windows 下仍然有对注册表访问的本机支持。以下代码在  Windows 7, 64 位  和  Windows 10, 64 位上进行了测试

Instead of using "Wow6432Node", which emulates a node by mapping one registry tree into another making it appear there virtually, you can do the follwing:

"Wow6432Node"您可以执行以下操作,而不是使用,它通过将一个注册表树映射到另一个注册表树使其虚拟出现在那里来模拟节点:

Decide, whether you need to access the 64 bit or the 32 bit registry, and use it as described below. You may also use the code I mentioned later (Additional information section), which creates a union query to get registry keys from both nodes in one query - so you can still query them by using their real path.

决定是否需要访问 64 位或 32 位注册表,并按如下所述使用它。您也可以使用我稍后提到的代码(附加信息部分),它创建一个联合查询以从一个查询中的两个节点获取注册表项 - 因此您仍然可以使用它们的真实路径来查询它们。

64 bit registry

64位注册表

To access the 64 bit registry, you can use RegistryView.Registry64as follows:

要访问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));

32 bit registry

32位注册表

If you want to access the 32bit registry, use RegistryView.Registry32as follows:

如果要访问32位注册表,使用RegistryView.Registry32如下:

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

Don't be confused, both versions are using Microsoft.Win32.RegistryHive.LocalMachineas first parameter, you make the distinction whether to use 64 bitor 32 bitby the 2nd parameter(RegistryView.Registry64versus RegistryView.Registry32).

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

Notethat

注意的是

  • On a 64bit Windows, HKEY_LOCAL_MACHINE\Software\Wow6432Nodecontains values used by 32 bit applications running on the 64 bit system. Only true 64 bit applications store their values in HKEY_LOCAL_MACHINE\Softwaredirectly. The subtree Wow6432Nodeis entirely transparent for 32 bit applications, 32 bit applications still see HKEY_LOCAL_MACHINE\Softwareas they expect it (it is a kind of redirection). In older versions of Windows as well as 32 bit Windows 7 (and Vista 32 bit) the subtree Wow6432Nodeobviously does notexist.

  • Due to a bug in Windows 7 (64 bit), the 32 bit source code version always returns "Microsoft" regardless which organization you have registered while the 64 bit source code version returns the right organization.

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

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

Coming back to the example you've provided, do it the following way to access the 64 bit branch:

回到您提供的示例,通过以下方式访问 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");


Additional information - for practical use:

附加信息 - 实际使用:

I'd like to add an interesting approach Johny Skovdalhas suggested in the comments, which I've picked up to develop some useful functions by using his approach: In some situations you want to get back all keys regardless whether it is 32 bit or 64 bit. The SQL instance names are such an example. You can use a union query in that case as follows (C#6 or higher):

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

// using Microsoft.Win32;
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);
}

public static IEnumerable<string> GetRegKeyNames(RegistryView view, string regPath,
                   RegistryHive hive = RegistryHive.LocalMachine)
{
    return RegistryKey.OpenBaseKey(hive, view)
        ?.OpenSubKey(regPath)?.GetSubKeyNames(); 
}

public static IEnumerable<string> GetAllRegKeyNames(string RegPath,
                                  RegistryHive hive = RegistryHive.LocalMachine)
{
    var reg64 = GetRegKeyNames(RegistryView.Registry64, RegPath, hive);
    var reg32 = GetRegKeyNames(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);
}

Now you can simply use the functions above as follows:

现在您可以简单地使用上述功能,如下所示:

Example 1:Get SQL instance names

示例 1:获取 SQL 实例名称

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

will give you a list of the value names and values in sqlRegPath.

将为您提供 sqlRegPath 中的值名称和值的列表。

Note:You can access the defaultvalue of a key (displayed by the commandline tool REGEDT32.EXEas (Default)) if you omit the ValueNameparameter in the corresponding functions above.

注意:如果在上面的相应函数中省略参数,则可以访问键的默认值(命令行工具显示REGEDT32.EXE(Default)ValueName

To get a list of SubKeyswithin a registry key, use the function GetRegKeyNamesor GetAllRegKeyNames. You can use this list to traverse further keys in the registry.

为了得到列表的子项注册表项中,使用功能GetRegKeyNamesGetAllRegKeyNames。您可以使用此列表遍历注册表中的其他键。

Example 2:Get uninstall information of installed software

示例二:获取已安装软件的卸载信息

var currentVersionRegPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $@"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);

will get all 32 bit and 64 bit uninstall keys.

将获得所有 32 位和 64 位卸载密钥。

Notice the null handlingrequired in the functions because SQL server can be installed as 32 bit or as 64 bit (Example 1 above). The functions are overloaded so you can still pass the 32 bit or 64 bit parameter if required - however, if you omit it then it will try to read 64 bit, if that fails (null value), it reads the 32 bit values.

请注意函数中所需的空处理,因为 SQL Server 可以安装为 32 位或 64 位(上面的示例 1)。这些函数已重载,因此您仍然可以根据需要传递 32 位或 64 位参数 - 但是,如果您省略它,那么它将尝试读取 64 位,如果失败(空值),它将读取 32 位值。

There is one speciality here: Because GetAllRegValueNamesis usually used in a loop context (see Example 1 above), it returns an empty enumerable rather than nullto simplify foreachloops: if it wouldn't be handled that way, the loop would have to be prefixed by an ifstatement checking for nullwhich would be cumbersome having to do that - so that is dealt with once in the function.

这里有一个特点:因为GetAllRegValueNames通常用在循环上下文中(参见上面的示例 1),它返回一个空的可枚举而不是null简化foreach循环:如果不以这种方式处理,循环必须以一个if语句检查null哪个必须这样做很麻烦 - 所以在函数中处理一次。

Why bothering about null?Because if you don't care, you'll have a lot more headaches finding out why that null reference exception was thrown in your code - you'd spend a lot of time finding out where and why it happened. And if it happened in production you'll be very busy studying log files or event logs (I hope you have logging implemented) ... better avoid null issues where you can in a defensive way. The operators ?., ?[...]and ??can help you a lot (see the code provided above). There is a nice related article discussing the new nullable reference types in C#, which I recommend to read and also this oneabout the Elvis operator.

为什么要为 null 烦恼?因为如果你不关心,你会更头疼地找出为什么在你的代码中抛出空引用异常 - 你会花很多时间找出它发生的位置和原因。如果它发生在生产中,您将非常忙于研究日志文件或事件日志(我希望您实现了日志记录)……最好以防御方式避免空问题。运营商?.?[...]并且??可以帮助你很多(见上面提供的代码)。有一篇很好的相关文章讨论了C# 中新的可为空引用类型,我推荐阅读这篇文章,还有这篇关于 Elvis 运算符的文章。



Hint:You can use the free edition of Linqpadto test all examples under Windows. It doesn't require an installation. Don't forget to press F4and enter Microsoft.Win32in the Namespace import tab. In Visual Studio, you require using Microsoft.Win32;at the top of your code.

提示:您可以使用免费版的Linqpad来测试 Windows 下的所有示例。它不需要安装。不要忘记按下F4并输入Microsoft.Win32Namespace 导入选项卡。在 Visual Studio 中,您需要using Microsoft.Win32;在代码的顶部。

Tip:To familiarize yourself with the new null handling operators,try out (and debug) the following code in LinqPad:

提示:要熟悉新的空处理运算符,请在 LinqPad 中尝试(并调试)以下代码:

Example 3:Demonstrating null handling operators

示例 3:演示空处理运算符

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>"
}

Try it with .Net fiddle

用 .Net fiddle 试试

If you're interested, hereare some examples I put together showing what else you can do with the tool.

如果你有兴趣,这里有一些例子我放在一起展示你可以用工具做什么。

回答by Sandra

I don't have enough rep to comment, but it's worth pointing out that it works when opening a remote registry using OpenRemoteBaseKey. Adding the RegistryView.Registry64 parameter allows a 32-bit program on Machine A to access the 64-bit registry on Machine B. Before I passed that parameter, my program was reading the 32-bit after OpenRemoteBaseKey, and did not find the key I was after.

我没有足够的代表来评论,但值得指出的是,它在使用 OpenRemoteBaseKey 打开远程注册表时有效。添加RegistryView.Registry64参数可以让A机上的32位程序访问B机上的64位注册表。 在我传递该参数之前,我的程序在OpenRemoteBaseKey之后读取了32位,并没有找到我的密钥之后。

Note: In my test, the remote machine was actually my machine, but I accessed it via OpenRemoteBaseKey, just as I would for a different machine.

注意:在我的测试中,远程机器实际上是我的机器,但我通过 OpenRemoteBaseKey 访问它,就像我访问另一台机器一样。

回答by Martin Prikryl

If you cannot use .NET 4 with its RegistryKey.OpenBaseKey(..., RegistryView.Registry64), you need to use Windows API directly.

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

The minimal interop is like:

最小的互操作是这样的:

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

Use it like:

像这样使用它:

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

回答by Silny ToJa

From what I have read and from my own tests, it seems to me that registry should be checked in this path "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall". Because in other paths the registers are not deleted after uninstalling the program.

从我阅读的内容和我自己的测试来看,在我看来,应该在“SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall”路径中检查注册表。因为在其他路径中,卸载程序后不会删除寄存器。

In this way I got 64 registers with 32 bit configuration.

通过这种方式,我得到了 64 个具有 32 位配置的寄存器。

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

For 32 registers is:

对于 32 个寄存器是:

registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);