Skip to content

Instantly share code, notes, and snippets.

@benvillalobos
Last active January 19, 2024 14:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benvillalobos/041673b9a73bec60fdc3bf0f86fae62a to your computer and use it in GitHub Desktop.
Save benvillalobos/041673b9a73bec60fdc3bf0f86fae62a to your computer and use it in GitHub Desktop.
How MSBuild Manifest Resource Naming Works

Manifest Resource Names and You!

For explicit information on how resource names are chosen, see this dotnet docs issue.

This gist is meant to be a slightly higher level explanation on how resource names are used. Mostly I'm just documenting as much context as possible before I forget.

So we start with some Foo.resx

That Foo.resx will get passed to a few tasks in different forms throughout the build.

Relevant tasks:

  • CreateManifestResourceName
  • GenerateResource (aka resgen)
  • CSC (CSharp Compiler, Roslyn)

CreateManifestResourceName Task

This task's entire purpose is to set the ManifestResourceName metadata for each Foo.resx (or relevant resource). Note that the tasks CreateCSharpManifestResourceName and CreateVisualBasicManifestResourceName are wrappers around the CreateManifestResourceName task.

The task is called in Microsoft.CSharp.CurrentVersion.targets. It ONLY runs if ManifestResourceName metadata is NOT set. Should you choose to set that manually, .resources will be appended to it and that will be the resource name and file name. Resulting in <your_manifestresourcename_value>.resources

Whether or not this task runs, the resources are eventually passed to the GenerateResource task.

GenerateResource Task

Takes in .Resx files (XML files) and spits out .resources files (binary files that can be read by the ResourceManager). ManifestResourceName controls the name that those files get spit out as (which comes from CreateManifestResourceName), UNLESS LogicalName metadata is set for this resource.

CSC Task

The .resources that was output from the GenerateResource task then gets passed over to the Csc task within the CoreCompile target (linked above).

The command line args for the Csc task are massively long, but here's the relevant line (taken from a test project's binlog):

/resource:obj\Debug\netcoreapp3.1\ConsoleApp1.Resource1.resources,ThisIsTheWholeNameSetAsLogicalName

Note: Here we have a resource named ConsoleApp1.Resource1.resources and a logical name separated by a comma, ThisIsTheWholeNameSetAsLogicalName. The compiler will ALWAYS prefer the logical name over whatever the manifest resource name was set to. It will then stuff that into the binary. In this case, the resource would be stuffed into the binary as ThisIsTheWholeNameSetAsLogicalName. Also note that if no LogicalName metadata was set, the embedded resource would be ConsoleApp1.Resource1.resources.

Again, see this dotnet docs issue for context and a more detailed explanation on how these manifest resource names get chosen.

See this mediocre diagram below for a visualization of what happens (pictures help me understand this stuff easier).

ResourceManifestNames

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment