9

From the .NET APIs catalog, I understand that the Microsoft.Win32.Registry class is declared in the .NET Standard + Platform Extensions 2.0 package in an assembly Microsoft.Win32.Registry, Version=4.1.1.0, PublicKeyToken=b03f5f7f11d50a3a.

enter image description here

I've created a class library which targets .NET Standard 2.0, and here's a simple class:

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

I've created a .NET Framework 4.7.2 console application which references my above class library:

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

When I run this on Windows, this throws a run-time exception:

System.IO.FileNotFoundException: 'Could not load file or assembly 'Microsoft.Win32.Registry, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'

Based on what I've read, having assembly load issues in this scenario is somewhat of a known issue. The prescribed work-around is to enable automatic binding redirects and make sure my .NET Framework application is using PackageReference rather than Project.Config. I have done this with my projects from which I shared the above code, but I'm still getting the error. What confuses me most, though, is that the error is indicating the .NET Core / .NET Core + Platform Extensions assembly (Version=4.1.3.0, PublicKeyToken=b03f5f7f11d50a3a) rather than the .NET Standard (Version=4.1.1.0, PublicKeyToken=b03f5f7f11d50a3a) or .NET Framework (Version=4.0.0.0, PublicKeyToken=b77a5c561934e089) versions from the APIs catalog:

enter image description here

This is further corroborated by the Microsoft.Win32.Registry.DLL that is in the output directory:

enter image description here

Based on further reading, I can make a little progress by doing either of the following:

  • Add <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> to the .NET Standard class library proj file

-- or --

  • Add the Microsoft.Win32.Registry NuGet package directly to my .NET Framework console application.

Either of these results in loading some version of the assembly, but then I get some odd behavior: I get an NRE for the LocalMachine property, which shouldn't happen.

enter image description here


So here are the questions:

1.) Since my project is a .NET Framework application, why is it not using the Microsoft.Win32.Registry class in the .NET Framework API, specifically the mscorlib assembly that the same APIs catalog refers to?

enter image description here

2.) Why isn't the "work around" in the GitHub post not working for me?

3.) Why is it seemingly looking for the .NET Core / ... extensions version of the assembly?

4.) Why when I explicitly export the NuGet Microsoft.Win32.Registry assembly in the .NET Standard class library or directly reference the package in the .NET Framework console application does it result in the strange behavior where Microsoft.Win32.Registry.LocalMachine is null, which should never be the case on a Windows machine?

rory.ap
  • 34,009
  • 10
  • 83
  • 174
  • when I add the Microsoft.Win32.Registry NuGet package to the console app it runs fine – magicandre1981 Jan 25 '20 at 15:14
  • Marked as favorite and would be taking a detailed look at it later !! Was wondering if you had made sure to uncheck `Prefer 32bit` in project properties. I have encountered issues reading registries in the past – Clint Jan 26 '20 at 18:02
  • Strange, i've tried both of the scenarios with installed nuget in console app and without installed - in both cases ran without issues. It may sound strange but sometimes restarting visual studio helps. – vasil oreshenski Jan 27 '20 at 15:21
  • @vasiloreshenski this isn't a new issue, it's something I've been dealing with for months. I've restarted visual studio hundreds of times, incidentally. – rory.ap Jan 27 '20 at 17:14
  • @vasiloreshenski -- curious, what happens if you create the console app in a new VS solution and reference the class library in the old solution from it? – rory.ap Jan 27 '20 at 17:17
  • @rory.ap If you mean add reference to the dll file, then the nuget dll is not present in the bin of the new project, but after installing the nuget in the new console app it ran without issue. – vasil oreshenski Jan 27 '20 at 17:24
  • @rory.ap Your picture which shows the details of Microsoft.Win32.Registry.dll is exactly the same as the one loaded on my test project (Console App NET472) - it is strange that says .NET Core in the product name, but it is working on my machine. – vasil oreshenski Jan 27 '20 at 18:30
  • @vasiloreshenski I know it can be fixed by adding the ref to the console app, but I don't want to have to do that everywhere I want to use my library. The references are supposed to flow, as they do when the library and console app are in the same solution. – rory.ap Jan 27 '20 at 21:16
  • Yes i understand what u mean. The best thing you can do is not to reference the dll but to include the project in the new solution (you can have one projected included in several solutions) and reference the project in the new console app. At work we have local nuget server and we usually create nuget package with the right dependencies. About the NRE exception i really don't have any idea what could be ... – vasil oreshenski Jan 27 '20 at 21:50
  • By any chance, @rory.ap, by using target framework ".NET Standard 2.0 + Platform Extensions" in your library, do you mean target framework "netstandard2.0", with the Microsoft.Windows.Compatibility NuGet package installed on top of that? I found that part of your question confusing when looking at this, and wanted to make sure my answer was useful. – Andy Mudrak Jan 28 '20 at 12:21
  • @AndyMudrak No I'm referring to the .NET Standard 2.0 platform extensions, as shown in the screenshots in my post from https://apisof.net/catalog/Microsoft.Win32.Registry – rory.ap Jan 28 '20 at 15:40
  • @rory.ap, Understood. But I am confused about exactly what this is. I don't see any options to target such a framework (i.e. settings the TargetFramework property in the .csproj file, nor in the UI for the settings on the project). I also didn't find any reference to what "Platform Extensions" are or what ".NET Standard 2.0 platform extensions" are, except on this catalog page you referenced. I guess I'm wondering, starting from scratch in VS2019, how do you set up a project for ".NET Standard 2.0 + Platform Extensions"? – Andy Mudrak Jan 28 '20 at 17:03

1 Answers1

3

The following answers make an assumption of having the newest version of Visual Studio 2019 (v16.4.3 at time of writing) installed, as this may have some effect on the outcome.

Question 1):

1.) Since my project is a .NET Framework application, why is it not using the Microsoft.Win32.Registry class in the .NET Framework API, specifically the mscorlib assembly that the same APIs catalog refers to?

This will actually use the v4.0.0.0 mscorlib Registry class when the projects are set up in either of the following manners:

  • Option 1

    • Class library: Target Framework = netstandard2.0, NuGet packages = Microsoft.Windows.Registry (v4.5.0)
    • Console app: Target Framework = net472, NuGet is set to "packages.config" mode, NuGet packages = Microsoft.Windows.Registry (v4.5.0) [and also AccessControls and Principal.Windows, as they are dependencies]
      • NOTE: Here, if you don't add the Microsoft.Windows.Registry package, you typically will get the runtime error looking for version 4.1.1.0 of the Registry dll. But I believe the version it looks for is based on what the current .NET Core SDK version you have installed.
  • Option 2 [I think this is the one you really want]

    • Class library: Target Framework = netstandard2.0, NuGet packages = Microsoft.Windows.Registry (v4.5.0)
    • Console app: Target Framework = net472, NuGet is set to "PackageReference" mode, NuGet packages = None
      • NOTE: In VS2019, if you have the option for "Allow format selection on first package install" checked, then it will allow you to choose to use the PackageReference style, where NuGet packages are referenced in the project file instead of packages.config. Typically you have to install any one NuGet package just to set this mode, but afterward can uninstall that package and it will stay in that mode. I believe you could also set the default mode as well, before you first create your net472 project.
      • NOTE: Here, the PackageReference mode seems to help resolve the NuGet dependencies on the other .NET Standard 2.0 class library, where as the package.config mode requires you to do it yourself it seems.

This should be easily reproducible, however, things that can cause some sort issue can be any of: - older versions of VS2019 being used - skipping binding redirects setting turned on for NuGet in VS - auto binding redirects turned off for the .NET 4.7.2 project - not "rebuilding" the solution after package or reference changes - not restarting the computer after installing/updating .NET SDK's or VS2019 updates - still having a packages.config file

I'd also like to note that in Option 1 above, I found in testing this out that if you don't add the Microsoft.Windows.Registry package, it fails on runtime looking for version 4.1.1.0 of the registry dll. But, I was able to get it to fail looking for runtime 4.1.3.0 by first installing Microsoft.Windows.Registry 4.7.0, and then I uninstalled it (thereby leaving the two dependent packages AccessControl and Principal.Windows), and without rebuilding the project: if I run it, it fails on runtime with the 4.1.3.0 version being the one it's looking for. Rebuilding it reverts back to 4.1.1.0. This remains the same even if I remove the two dependent packages. Note: this also works if you simply remove the references to the dll's in the project, rather than uninstalling the NuGet packages.

Question 2):

2.) Why isn't the "work around" in the GitHub post not working for me?

I have a feeling this is happening because you may have an older version of VS2019 than 16.4.3. I found that when I was using an older version, the PackageReference mode still resulted in the runtime error. When I updated (sorry, I am not sure which exact revision actually fixes it) VS2019 to 16.4.3, this seems to now just work. I am not sure if this is some sort of unexpected interaction with the various SDK's (perhaps some being more recently released but not supported by an older revision of VS). It could also be an issue if the packages.config file is still lingering around.

Another issue could potentially be interference by other NuGet packages that may be installed and have different library version requirements.

Question 3):

3.) Why is it seemingly looking for the .NET Core / ... extensions version of the assembly?

In a .NET 4.7.2 project that references a .NET Standard 2.0 (.NET standard projects are .NET Core projects by default), it will utilize the .NET Core framework, not the .NET framework. So any references to the Registry are not by default available. You need the Microsoft.Windows.Registry packages (at the least) to allow use of the Registry, which I believe has the ability to act as a shim to the .NET 4.7.2 mscorlib verison of the library if available, but use the 4.1.1.0 version as a fallback (or 4.1.3.0 version if you're referencing from a .NET Core project instead).

Question 4):

4.) Why when I explicitly export the NuGet Microsoft.Win32.Registry assembly in the .NET Standard class library or directly reference the package in the .NET Framework console application does it result in the strange behavior where Microsoft.Win32.Registry.LocalMachine is null, which should never be the case on a Windows machine?

I haven't personally tested this, and didn't run into this issue when I tested the above, but my feeling on this is that the dll's are likely missing their dependent dll's. But thinking about that further, would likely just result in another runtime error if that's the case. I think the issue is that they aren't intended to be directly exported and something may be missing along the way.

I'd also note that if you run this on any platform other than Windows, the registry is likely to come back as null since I think it wouldn't exist on, say, a Linux runtime.

Other Notes:

I get a general sense that this sort of thing has been a little buggy with VS and .NET Core in general referencing to/from .NET Framework, and that there's progress being made regularly to improve this.

I found also that there are some surprising issues I ran into that I didn't expect. For example, creating a .NET Standard console app, referencing the .NET Standard class library, and still getting the runtime error, no matter what packages I installed on the console app. You would think the exact same target framework would just work without any special configurations, but it doesn't seem to. But if you create a .NET Core console app instead, it does work properly. It's a bit mystifying, but there's always a technical explanation somewhere in the mix.

Andy Mudrak
  • 787
  • 3
  • 7