为什么我的 .NET 框架应用程序会寻找错误版本的 .NET 核心/标准平台扩展程序集,我该如何解决?

ror*_*.ap 8 .net c# binding .net-core .net-standard

.NET API 目录中,我了解到Microsoft.Win32.Registry该类是在.NET Standard + Platform Extensions 2.0程序集的包中声明的Microsoft.Win32.Registry, Version=4.1.1.0, PublicKeyToken=b03f5f7f11d50a3a

在此处输入图片说明

我创建了一个面向 .NET Standard 2.0 的类库,这是一个简单的类:

public class NetStandardClass
{
    public string GetHklmRegValue()
    {
        var lmKey = Microsoft.Win32.Registry.LocalMachine;
        var softwareKey = lmKey.OpenSubKey("Software"); 
        return "value";
    }
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个 .NET Framework 4.7.2 控制台应用程序,它引用了我上面的类库:

class Program
{
    static void Main(string[] args)
    {
        string value = new ClassLibrary2.NetStandardClass().GetHklmRegValue();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我在 Windows 上运行它时,这会引发一个运行时异常:

System.IO.FileNotFoundException: '无法加载文件或程序集 'Microsoft.Win32.Registry, Version= 4.1.3.0 , Culture=neutral, PublicKeyToken= b03f5f7f11d50a3a ' 或其依赖项之一。该系统找不到指定的文件。'

根据我所读到的内容,在这种情况下出现程序集加载问题在某种程度上是一个已知问题。规定的解决方法是启用自动绑定重定向并确保我的 .NET Framework 应用程序使用PackageReference而不是Project.Config. 我已经在我共享上述代码的项目中做到了这一点,但我仍然收到错误消息。什么最让我困惑,虽然是错误的指示.NET Core/.NET Core + Platform Extensions组件(版本= 4.1.3.0,公钥= b03f5f7f11d50a3a),而不是.NET标准(版本= 4.1.1.0,公钥= b03f5f7f11d50a3a)或.NET框架(版本=4.0.0.0, PublicKeyToken=b77a5c561934e089 ) 来自 API 目录的版本:

在此处输入图片说明

输出目录中的 Microsoft.Win32.Registry.DLL 进一步证实了这一点:

在此处输入图片说明

根据进一步阅读,我可以通过执行以下任一操作来取得一些进展:

  • 添加<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>到 .NET Standard 类库项目文件

- 或者 -

  • Microsoft.Win32.RegistryNuGet 包直接添加到我的 .NET Framework 控制台应用程序。

这些都会导致加载某个版本的程序集,但随后我得到了一些奇怪的行为:我获得了该LocalMachine属性的 NRE ,这不应该发生。

在此处输入图片说明


所以这里是问题:

1.) 既然我的项目是.NET Framework 应用程序,为什么不使用Microsoft.Win32.Registry.NET Framework API 中的类,特别mscorlib是同一个API 目录引用的程序集?

在此处输入图片说明

2.) 为什么 GitHub 帖子中的“解决方法”对我不起作用?

3.) 为什么它似乎在寻找程序集的 .NET Core / ... 扩展版本?

4.) 为什么当我显式导出Microsoft.Win32.Registry.NET Standard 类库中的 NuGet程序集或直接引用 .NET Framework 控制台应用程序中的包时,它会导致奇怪的行为 whereMicrosoft.Win32.Registry.LocalMachine为 null,这在 Windows 上绝不应该是这种情况机器?

小智 3

以下答案假设安装了最新版本的 Visual Studio 2019(撰写本文时为 v16.4.3),因为这可能会对结果产生一些影响。

问题1):

1.) 由于我的项目是 .NET Framework 应用程序,为什么它不使用 .NET Framework API 中的 Microsoft.Win32.Registry 类,特别是相同 API 目录引用的 mscorlib 程序集?

Registry当通过以下任一方式设置项目时,这实际上将使用 v4.0.0.0 mscorlib类:

  • 选项1

    • 类库:目标框架 = netstandard2.0,NuGet 包 = Microsoft.Windows.Registry (v4.5.0)
    • 控制台应用程序:目标框架= net472,NuGet设置为“packages.config”模式,NuGet包= Microsoft.Windows.Registry(v4.5.0)[还有AccessControls和Principal.Windows,因为它们是依赖项]
      • 注意:在这里,如果您不添加 Microsoft.Windows.Registry 包,通常会在查找版本 4.1.1.0 的注册表 dll 时出现运行时错误。但我相信它寻找的版本是基于您当前安装的 .NET Core SDK 版本。
  • 选项2 [我认为这是你真正想要的]

    • 类库:目标框架 = netstandard2.0,NuGet 包 = Microsoft.Windows.Registry (v4.5.0)
    • 控制台应用程序:目标框架 = net472,NuGet 设置为“PackageReference”模式,NuGet 包 = None
      • 注意:在 VS2019 中,如果选中“允许在首次安装包时选择格式”选项,那么它将允许您选择使用 PackageReference 样式,其中在项目文件中引用 NuGet 包而不是packages.config。通常,您必须安装任何一个 NuGet 包才能设置此模式,但之后可以卸载该包,它将保留在该模式下。我相信您也可以在首次创建 net472 项目之前设置默认模式。
      • 注意:在这里,PackageReference 模式似乎有助于解决 NuGet 对其他 .NET Standard 2.0 类库的依赖关系,而 package.config 模式似乎需要您自己完成。

这应该很容易重现,但是,可能导致某种问题的原因可能是: - 使用旧版本的 VS2019 - 跳过在 VS 中为 NuGet 打开的绑定重定向设置 - 为 .NET 4.7 关闭自动绑定重定向。 2 项目 - 在包或引用更改后不“重建”解决方案 - 安装/更新 .NET SDK 或 VS2019 更新后不重新启动计算机 - 仍然有一个 packages.config 文件

我还想指出,在上面的选项 1中,我在测试中发现,如果不添加 Microsoft.Windows.Registry 包,它在运行时查找 4.1.1.0 版本的注册表 dll 时会失败。但是,通过首先安装 Microsoft.Windows.Registry 4.7.0,然后卸载它(从而留下两个依赖包 AccessControl 和 Primary.Windows),并且无需重建,我能够使其在查找运行时 4.1.3.0 时失败项目:如果我运行它,它会在运行时失败,4.1.3.0 版本是它正在寻找的版本。重建它会恢复到 4.1.1.0。即使我删除了两个依赖包,这仍然是一样的。注意:如果您只是删除项目中对 dll 的引用,而不是卸载 NuGet 包,这也适用。

问题2):

2.) 为什么 GitHub 帖子中的“解决方法”对我不起作用?

我感觉发生这种情况是因为您的 VS2019 版本可能比 16.4.3 旧。我发现当我使用旧版本时,PackageReference模式仍然会导致运行时错误。当我将 VS2019 更新到 16.4.3 时(抱歉,我不确定哪个确切的版本真正修复了它),这似乎现在可以工作了。我不确定这是否是与各种 SDK 的某种意外交互(也许有些是最近发布的,但 VS 的旧版本不支持)。如果packages.config 文件仍然存在,也可能是一个问题。

另一个问题可能是受到可能已安装且具有不同库版本要求的其他 NuGet 包的干扰。

问题3):

3.)为什么它似乎在寻找程序集的.NET Core / ...扩展版本?

在引用 .NET Standard 2.0 的 .NET 4.7.2 项目中(.NET 标准项目默认为 .NET Core 项目),它将利用 .NET Core 框架,而不是 .NET 框架。因此,默认情况下对注册表的任何引用都不可用。您需要 Microsoft.Windows.Registry 包(至少)来允许使用注册表,我相信它能够充当库的 .NET 4.7.2 mscorlib 版本的填充程序(如果可用),但请使用4.1.1.0 版本作为后备(或者 4.1.3.0 版本,如果您从 .NET Core 项目引用)。

问题4):

4.) 为什么当我显式导出 .NET Standard 类库中的 NuGet Microsoft.Win32.Registry 程序集或直接引用 .NET Framework 控制台应用程序中的包时,会导致 Microsoft.Win32.Registry.LocalMachine 出现奇怪的行为为 null,在 Windows 机器上绝对不应该出现这种情况?

我没有亲自测试过这一点,并且在测试上述内容时也没有遇到这个问题,但我对此的感觉是 dll 可能缺少其依赖的 dll。但进一步考虑一下,如果是这种情况,很可能会导致另一个运行时错误。我认为问题在于它们不打算直接导出,并且在此过程中可能会丢失一些东西。

我还要指出的是,如果您在 Windows 以外的任何平台上运行它,注册表可能会返回为空,因为我认为它不会存在于 Linux 运行时上。

其他注意事项:

我的总体感觉是,VS 和 .NET Core 在一般引用 .NET Framework 或从 .NET Framework 引用时存在一些问题,并且定期取得进展来改进这一点。

我还发现有一些我没有预料到的令人惊讶的问题。例如,创建 .NET Standard 控制台应用程序、引用 .NET Standard 类库,但无论我在控制台应用程序上安装什么包,仍然会出现运行时错误。您可能会认为完全相同的目标框架无需任何特殊配置即可工作,但事实似乎并非如此。但如果您创建一个 .NET Core 控制台应用程序,它确实可以正常工作。这有点令人费解,但其中总有一个技术解释。