dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.23k stars 1.35k forks source link

"MSB3823: Non-string resources require the property GenerateResourceUsePreserializedResources to be set to true" if TargetFramework=net48 #4704

Closed to-st closed 5 years ago

to-st commented 5 years ago

We have a few projects that muti target .net core and the old fashioned .net framework. Some of these include binary resources (mostly images). With .net core 2.1 / 2.2 they build successfully but accessing an image while running under .net core throws an exception while with the full framework everything continues to works. Now .net core 3.0 added support for binary resources giving us the chance to eliminate a few workarounds. But now TargetFramework=net48 fails to build. As we are still in the transition to .net core and most of our products (still) require the full framework. Therefore this blocks any usage of .net core 3.0 in our codebase.

Steps to reproduce

unzip the attached project and run "dotnet build" or "dotnet build -p:TargetFramework=net48". Note that "dotnet build -p:TargetFramework=netcoreapp3.0" builds successfully.

LibraryWithImage.zip

Expected behavior

Successful build - net48 definitely supports images inside .resx

Actual behavior

Microsoft (R)-Build-Engine, Version 16.3.0-preview-19426-01+faf5e5d75 f▒r .NET Core
Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten.

  Wiederherstellung in "113,4 ms" f▒r "C:\source\ResxBug\LibraryWithImage\LibraryWithImage.csproj" abgeschlossen.
  You are using a preview version of .NET Core. See: https://aka.ms/dotnet-core-preview
  You are using a preview version of .NET Core. See: https://aka.ms/dotnet-core-preview
C:\Program Files\dotnet\sdk\3.0.100-preview9-014004\Microsoft.Common.CurrentVersion.targets(3056,5): error : MSB3823: Non-string resources require the property GenerateResourceUsePreserializedResources to be set to true. [C:\source\ResxBug\LibraryWithImage\LibraryWithImage.csproj]
C:\Program Files\dotnet\sdk\3.0.100-preview9-014004\Microsoft.Common.CurrentVersion.targets(3056,5): warning MSB3555: Die Ausgabedatei "C:\source\ResxBug\LibraryWithImage\obj\Debug\net48\LibraryWithImage.Resources.resources" ist m▒glicherweise besch▒digt. [C:\source\ResxBug\LibraryWithImage\LibraryWithImage.csproj]
  LibraryWithImage -> C:\source\ResxBug\LibraryWithImage\bin\Debug\netcoreapp3.0\LibraryWithImage.dll

Fehler beim Buildvorgang.

C:\Program Files\dotnet\sdk\3.0.100-preview9-014004\Microsoft.Common.CurrentVersion.targets(3056,5): warning MSB3555: Die Ausgabedatei "C:\source\ResxBug\LibraryWithImage\obj\Debug\net48\LibraryWithImage.Resources.resources" ist m▒glicherweise besch▒digt. [C:\source\ResxBug\LibraryWithImage\LibraryWithImage.csproj]
C:\Program Files\dotnet\sdk\3.0.100-preview9-014004\Microsoft.Common.CurrentVersion.targets(3056,5): error : MSB3823: Non-string resources require the property GenerateResourceUsePreserializedResources to be set to true. [C:\source\ResxBug\LibraryWithImage\LibraryWithImage.csproj]
    1 Warnung(en)
    1 Fehler

Verstrichene Zeit 00:00:02.58

Environment data

SDK 3.0.100-preview9-014004 installed using the installer (see https://dotnet.microsoft.com/download/dotnet-core/3.0 )

rainersigwald commented 5 years ago

This is an intentional change with .NET Core SDK 3.0. As you point out, the prior versions of MSBuild on .NET Core would produce corrupted output which would crash at runtime. With the new version, it's possible to embed resources when using .NET Core MSBuild, but because they're embedded in a different way, they require a new reference to access them at runtime: System.Resources.Extensions. This is available as a NuGet package which will be published to NuGet.org when .NET Core 3.0 is released. It's available now from a prerelease feed https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json.

to-st commented 5 years ago

just to make sure: It is intentional that if you have a project with the new csproj format, binary resources, a PackageReference to System.Resources.Extensions and add net48 as targetframework it won't built using "dotnet build"? I do not complain that netcoreapp2.1 no longer builds broken dll ;)

I assume support for binary resources in net48 is not dropped altogether, so how should I build a project with the new csproj format for net48? Calling the msbuild from the .net framework build tools directly?

rainersigwald commented 5 years ago

It is intentional that if you have a project with the new csproj format, binary resources, a PackageReference to System.Resources.Extensions and add net48 as targetframework it won't built using "dotnet build"?

Correct.

how should I build a project with the new csproj format for net48? Calling the msbuild from the .net framework build tools directly?

Yes: projects built using Visual Studio or MSBuild.exe (as opposed to dotnet build or dotnet msbuild) use the older approach to serialize resources into the output assembly by default. It's not ideal to have the project built two different ways depending on how you launched the build, though, so I would recommend always specifying GenerateResourceUsePreserializedResources=true in your projects and referencing System.Resources.Extensions. That unifies on the new codepath.

RusKnyaz commented 5 years ago

I did not quite understand. What if I just want my existing project to build successfully both on machines with netcore2.1 and on machines with netcore3.0? However, I do not want to change anything, and am ready to get resources through Assembly.GetManifestResourceStream.

RusKnyaz commented 5 years ago

I solved my issue by removing of the extra lines from Resources.resx file.

rainersigwald commented 5 years ago

What if I just want my existing project to build successfully both on machines with netcore2.1 and on machines with netcore3.0?

If your projects built successfully with the .NET Core SDK 2.1, they should work without modification in .NET Core SDK 3.0. If the projects built without errors but produced corrupt outputs in 2.1, they can be built correctly only with 3.0's new behavior.

figloalds commented 5 years ago

I did not quite understand. What if I just want my existing project to build successfully both on machines with netcore2.1 and on machines with netcore3.0? However, I do not want to change anything, and am ready to get resources through Assembly.GetManifestResourceStream.

I got mad too and I was about to just rage uninstall dotnet sdk 3.0, but then it was just add

<PackageReference Include="System.Resources.Extensions" Version="4.6.0" /> to references and <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources> to a property group

I have a few complaints though: GenerateResourceUsePreserializedResources is a HUGE and ugly name and also I wonder why and who would ever disable this, if it's the only way to get embed resources in SDK 3.0 to build? It looks unnecessary to add that ugly bigass name property to my csproj and the error message could have been: Hey, you just need to install System.Resources.Extensions from NuGet, we'll make the rest work. This over here is one of my biggest rants with .Net lately: Things keep breaking every time you upgrade the SDK. :\

figloalds commented 5 years ago

Actually after I applied the fixes suggested NOW the application causes runtime error with Unable to load file or assembly 'System.Resources.Extensions, Version=4.0.0 I'm uninstalling SDK 3.0 because it's causing a lot more problems than it should it Installed automatically with Visual Studio update and just broke a lot of code that used to work fine when building with dotnet cli 2.2.300 and even after trying to apply the changes suggested by the SDK it still caused more problems.

Edit: except it's not possible without removing the entire Web Development from Visual Studio altogether. So because of a broken update on Visual Studio I have to completely remove non-string resources from projects because my dotnet command will just build broken assemblies otherwise.

Edit 2: Fixed by going to C:\Program Files\dotnet\sdk and hardcore deleting 3.0.100;

MiguelAlho commented 4 years ago

Also noticed this fix won't work when targeting net45, since the System.Resources.Extensions package does not support it (minimum is netstandard2.0 which would be net461). I know net45 is old, but this app is an installer intended to run on machines that might not have any new version of .Net running (but will install net48 if needed).

mikhail-barg commented 4 years ago

@rainersigwald I just faced this issue, and while the fix with adding the package and setting the flag does work, it looks really weird that two build paths are actually different. Could this difference be mitigated or at least be transparently hidden from developers?

TylerBrinkley commented 4 years ago

I'm running into this issue when trying to migrate some of our libraries with WinForm assets to using the sdk-style csproj so that it can be integrated into CI/CD using the .NET CLI. Unfortunately, while adding the package and setting the flag allows the project to build I do get the following runtime exception when it tries to load the forms icon from the embedded resource even though the System.Resources.Extensions.dll is in the bin folder.

System.IO.FileLoadException: 'Could not load file or assembly 'System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)'

rainersigwald commented 4 years ago

@TylerBrinkley Can you get a fusion log trace of the attempted load, to see why the .NET Runtime is rejecting the reference?

TylerBrinkley commented 4 years ago

@rainersigwald I'm afraid I've moved on from this and am instead manually pulling in these resources using Assembly.GetManifestResourceStream. This also allowed me to not be forced to update the target framework to .NET 4.6.1. Thanks for reaching out though.

EamonHetherton commented 4 years ago

@rainersigwald I've got a fusion log trace for you:

The operation failed.
Bind result: hr = 0x80131040. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Running under executable  C:\Git\*******\bin\Debug\net48\*******.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
 (Fully-specified)
LOG: Appbase = file:///C:/Git/*******/bin/Debug/net48/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = *******.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Git\*******\bin\Debug\net48\*******.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Post-policy reference: System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///C:/Git/*******/bin/Debug/net48/System.Resources.Extensions.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\Git\*******\bin\Debug\net48\System.Resources.Extensions.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: System.Resources.Extensions, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
WRN: Comparing the assembly name resulted in the mismatch: Build Number
ERR: The assembly reference did not match the assembly definition found.
ERR: Run-from-source setup phase failed with hr = 0x80131040.
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

Also, a simple reproduction Solution

EamonHetherton commented 4 years ago

If I downgrade System.Resources.Extensions to 4.6.0 it works again.

valeriob commented 4 years ago

Thanks @EamonHetherton !

milang commented 4 years ago

I can confirm @EamonHetherton 's finding (and thank you for finding a work-around!)

When I use the latest System.Resources.Extensions NuGet package at version 4.7.1, compile works but runtime fails with:

System.IO.FileLoadException : Could not load file or assembly 'System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

This makes sense (as far as fusion loader goes) because System.Resources.Extensions.dll that gets copied to the build output directory is at version 4.0.1.0 (which isn't matching the 4.0.0.0 version rutime is trying to load):

// (from JetBrains dotPeek)
// Assembly System.Resources.Extensions, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// MVID: 52C506DF-7399-4EF0-B2DC-769C3C88FEE3
// Assembly references:
// mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
[assembly: AssemblyFileVersion("4.700.20.21406")]
[assembly: AssemblyInformationalVersion("3.1.4+c4164928b270ee2369808ab347d33423ef765216")]
[assembly: AssemblyVersion("4.0.1.0")]
...

When I downgrade to System.Resources.Extensions NuGet package at version 4.6.0, compile works + runtime works. This time the System.Resources.Extensions.dll that gets copied to the build output directory is at version 4.0.0.0:

// (from JetBrains dotPeek)
// Assembly System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// MVID: 4C478117-5922-4E23-84DD-CC9E7D793526
// Assembly references:
// netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
[assembly: AssemblyFileVersion("4.700.19.46214")]
[assembly: AssemblyInformationalVersion("3.0.0+4ac4c0367003fe3973a3648eb0715ddb0e3bbcea")]
[assembly: AssemblyVersion("4.0.0.0")]
...

Is this a known issue? Can we expect a fix @rainersigwald ?

adegeo commented 4 years ago

I too have run into this problem updating to .NET Core project style. Only version 4.6.0 of the extension package works at run time.

rainersigwald commented 4 years ago

Thanks, folks! I think this is a bug in System.Resources.Extensions and I filed https://github.com/dotnet/runtime/issues/39078 to track it. When a fix is available we can pick it up in MSBuild: https://github.com/microsoft/msbuild/issues/5504

marchewek commented 4 years ago

Hi everyone. To make it clear from the beginning - the differences and details between dotnet build , msbuild and vsbuild are not my area of expertise.

I'd like to achieve a VS-independent build to easily manage build agents:

There are two more problems to achieve VS-independency:

Thanks, Patryk

rainersigwald commented 4 years ago

@marchewek Can you please file new issues for your problems? Please be as specific as possible.

Questions I have from your comment:

  1. What is vsbuild? I'm not familiar with it.
  2. MSBuild.exe works fine with SDK-style projects. What leads you to think otherwise?
  3. What problems are you seeing with XAML builds? That SO question is quite old and predates .NET Core support for WPF.
  4. Have you tried using Microsoft.NETFramework.ReferenceAssemblies to handle targeting .NET 4.5?
PPenglis commented 4 years ago

System.Resources.Extensions can work if you use a binding redirect: In the app.config add:

<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Resources.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="4.0.0.0" newVersion="4.0.1.0" />
</dependentAssembly>    
</assemblyBinding>
</runtime>
rainersigwald commented 4 years ago

You won't have to do that manually after picking up a System.Resources.Extensions that includes https://github.com/dotnet/runtime/pull/39386.

HavenDV commented 3 years ago

4. Have you tried using Microsoft.NETFramework.ReferenceAssemblies to handle targeting .NET 4.5?

I am trying to release a version of a library for .Net Framework 4.5 besides .Net Core 3.1 and .Net 5, can you explain this in more detail? Simple usage doesn't work:

<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
C:\Users\runneradmin\AppData\Local\Microsoft\dotnet\sdk\5.0.100\Microsoft.Common.CurrentVersion.targets(3075,5): error MSB3823: Non-string resources require the property GenerateResourceUsePreserializedResources to be set to true. [D:\a\HtmlEditorControl\HtmlEditorControl\src\WinForms\Framework Extensions\Framework Extensions.csproj]
C:\Users\runneradmin\AppData\Local\Microsoft\dotnet\sdk\5.0.100\Microsoft.Common.CurrentVersion.targets(3075,5): error MSB3822: Non-string resources require the System.Resources.Extensions assembly at runtime, but it was not found in this project's references. [D:\a\HtmlEditorControl\HtmlEditorControl\src\WinForms\Framework Extensions\Framework Extensions.csproj]
PPenglis commented 3 years ago
  1. Have you tried using Microsoft.NETFramework.ReferenceAssemblies to handle targeting .NET 4.5?

I am trying to release a version of a library for .Net Framework 4.5 besides .Net Core 3.1 and .Net 5, can you explain this in more detail? Simple usage doesn't work:

<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
C:\Users\runneradmin\AppData\Local\Microsoft\dotnet\sdk\5.0.100\Microsoft.Common.CurrentVersion.targets(3075,5): error MSB3823: Non-string resources require the property GenerateResourceUsePreserializedResources to be set to true. [D:\a\HtmlEditorControl\HtmlEditorControl\src\WinForms\Framework Extensions\Framework Extensions.csproj]
C:\Users\runneradmin\AppData\Local\Microsoft\dotnet\sdk\5.0.100\Microsoft.Common.CurrentVersion.targets(3075,5): error MSB3822: Non-string resources require the System.Resources.Extensions assembly at runtime, but it was not found in this project's references. [D:\a\HtmlEditorControl\HtmlEditorControl\src\WinForms\Framework Extensions\Framework Extensions.csproj]

In your projects PropertyGroup set

<PropertyGroup>
....
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
</PropertyGroup>

If the other is still giving issues one could use GeneratePathProperty=true on the nuget package and then reference the dll directly:

<ItemGroup>
<Reference Name="System.Resources.Extensions" Include="$(Microsoft_NETFramework_ReferenceAssemblies)\lib\NET45\<<NameofDLL>>" />
</ItemGroup>
HavenDV commented 3 years ago

If the other is still giving issues one could use GeneratePathProperty=true on the nuget package and then reference the dll directly:

<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net45" Version="1.0.0" GeneratePathProperty="true">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<Reference Name="System.Resources.Extensions" Include="$(PkgMicrosoft_NETFramework_ReferenceAssemblies_net45)\build\.NETFramework\v4.5\Facades\System.Resources.ResourceManager.dll" />

I am trying this. But the package does not contain anything suitable other than System.Resources.ResourceManager.dll. Anyway, I get the same errors:

2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(3075,5): error MSB3822: Non-string resources require the System.Resources.Extensions assembly at runtime, but it was not found in this project's references.
rainersigwald commented 3 years ago

@HavenDV That was in reply to https://github.com/dotnet/msbuild/issues/4704#issuecomment-660149373 which reported some different issues. If you get this error

2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(3075,5): error MSB3822: Non-string resources require the System.Resources.Extensions assembly at runtime, but it was not found in this project's references.

you need to add

<PackageReference Include="System.Resources.Extensions" Version="5.0.0" />

to your project file.

rainersigwald commented 3 years ago

If the other is still giving issues one could use GeneratePathProperty=true on the nuget package and then reference the dll directly:

<ItemGroup>
<Reference Name="System.Resources.Extensions" Include="$(Microsoft_NETFramework_ReferenceAssemblies)\lib\NET45\<<NameofDLL>>" />
</ItemGroup>

@gooterz nit: System.Resources.Extensions is not in the reference assemblies, only its own package.

HavenDV commented 3 years ago

@HavenDV That was in reply to #4704 (comment) which reported some different issues. If you get this error

I'm looking for a way to add .Net 4.0 and .Net 4.5 targets to a multi-target project. .Net 4.6.1, .Net Core 3.1 and .Net 5 work fine.

rainersigwald commented 3 years ago

@HavenDV Ah, I see. Since System.Resources.Extensions only has .NET 4.6.1 and .NET Standard 2.0, it can't be used there. You can build a project that multitargets to older .NET versions with MSBuild.exe, but not with dotnet build.

martingertsen commented 2 years ago

@RusKnyaz

I solved my issue by removing of the extra lines from Resources.resx file.

What extra line did you remove? I'm having the same problem :-/

Luiz-Monad commented 2 years ago

Do I really need to create the 'resources.resx' for embedded resources to work ? I presumed they would just work by setting them on the .csproj. Is that the expected behavior ? can someone confirm it for me, please ?

martingertsen commented 2 years ago

I circumvented the problem by removing the reference to the System.Resources.Extension NuGet package. To get the project to build after that, I had to set GenerateResourceUsePreserializedResources to false. This made the project work fine, though I lost the localized ressources in the generated NuGet. I didn't find a solution for that, but fortunately I had just a few texts localized, so I changed to using a dictionary instead - suboptimal, I know, but after all this time consuming trial & error I was short on time, and I had to get a working solution fast, so yeah... this is where I ended up :-/

chuongmep commented 1 year ago

This problem still cause with .NET 6 Multi Platform