我有一个为AnyCPU编译的c#单元测试项目 . 我们的构建服务器是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_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server
在32位模式下运行的应用程序是否有办法访问Wow6432Node外部的注册表?
5 回答
创建/打开注册表项时,必须使用KEY_WOW64_64KEY参数 . 但AFAIK对于Registry类是不可能的,但仅在直接使用API时才有效 .
This可能有助于您入门 .
在使用 .NET Framework 4.x 的64位Windows下,仍然支持注册表访问 . 以下代码使用Windows 7,64位以及Windows 10,64位进行测试 . 要访问 64 bit registry ,您可以使用:
如果要访问 32bit registry ,请使用:
不要混淆,两个版本都使用
Microsoft.Win32.RegistryHive.LocalMachine
作为第一个参数,您可以区分 2nd parameter 或 2nd parameter (RegistryView.Registry64
与RegistryView.Registry32
)之间的 64 bit 或 32 bit .Note 那个
在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
显然确实存在 not .由于Windows 7(64位)中的错误,32位源代码版本始终返回“Microsoft”,无论您注册哪个组织,而64位源代码版本返回正确的组织 .
回到您提供的示例,按以下方式访问64位分支:
Additional information for practical use:
我想在评论中添加一个有趣的方法Johny Skovdal,我已经选择通过使用他的方法开发一些有用的函数:在某些情况下,无论是32位还是64位,你都想要取回所有键 . 位 . SQL实例名称就是这样一个例子 . 在这种情况下,您可以使用联合查询,如下所示(C#6或更高版本):
现在您可以简单地使用上面的函数,如下所示:
Example 1: 获取SQL实例名称
将为您提供sqlRegPath中值名称和值的列表 .
Note: 如果省略上述相应功能中的
ValueName
参数,则可以访问键的 default 值(由命令行工具REGEDT32.EXE
显示为(Default)
) .要在注册表项中获取 SubKeys 列表,请使用函数
GetRegKeyNames
或GetAllRegKeyNames
. 您可以使用此列表遍历注册表中的其他键 .Example 2: 获取已安装软件的卸载信息
将获得所有32位和64位卸载密钥 .
函数中需要 Notice the null handling ,因为SQL Server可以安装为32位或64位(上面的示例1) . 函数被重载,因此如果需要,您仍然可以传递32位或64位参数 - 但是,如果省略它,它将尝试读取64位,如果失败(空值),它将读取32位值 .
这里有一个特性:因为 GetAllRegValueNames 通常用在循环上下文中(参见上面的例1),它返回一个空的枚举而不是
null
来简化foreach
循环:如果它不会被这样处理,循环将不得不以if
语句为前缀检查null
,这样做很麻烦 - 所以在函数中处理一次 .Why bothering about null? 因为如果你没有更多的麻烦找出为什么在你的代码中引发了空引用异常 - 你'd spend a lot of time finding out where and why it happened. And if it happened in production you'将非常忙于研究日志文件或事件日志(我希望你已经实现了日志记录)...更好的避免你可以采取防御方式的空问题 . 运营商
?.
,?[
...]
和??
可以提供帮助你很多(见上面提供的代码) . 有一篇很好的相关文章讨论了新的nullable reference types in C#,我建议阅读,以及this one关于Elvis算子 .Hint: 您可以使用 Linqpad 的免费版来测试Windows下的所有示例 . 它没有忘记按F4并在命名空间导入选项卡中输入
Microsoft.Win32
. 在Visual Studio中,您需要在代码顶部使用using Microsoft.Win32;
.Tip: 要熟悉新的 null handling operators, 试用(并调试)LinqPad中的以下代码:
Example 3: 演示空处理运算符
Try it with .Net fiddle
如果您有兴趣, here 是我放在一起的一些示例,展示了您可以使用该工具做些什么 .
我没有足够的回复评论,但值得指出的是,它在使用OpenRemoteBaseKey打开远程注册表时有效 . 添加RegistryView.Registry64参数允许机器A上的32位程序访问机器B上的64位注册表 . 在我传递该参数之前,我的程序在OpenRemoteBaseKey之后读取了32位,并且没有找到密钥I之后 .
注意:在我的测试中,远程计算机实际上是我的计算机,但我通过OpenRemoteBaseKey访问它,就像我对不同的计算机一样 .
试试这个(来自32位进程):
(发现here) .
如果您不能将.NET 4与RegistryKey.OpenBaseKey(..., RegistryView.Registry64)一起使用,则需要直接使用Windows API .
最小互操作如下:
使用它像: