ILDasm, mscorlib and System.Runtime decompilation differences depending on the directory

StackOverflow https://stackoverflow.com/questions/22743434

  •  24-06-2023
  •  | 
  •  

Question

I have been playing around with ILDasm and have noticed that:

  • Decompiling C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.dll (36KB) simply returns a manifest file. Decompiling C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.dll (114KB) returns the manifest and all types in the assembly.

  • Decompiling C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\mscorlib.dll (38KB) simply returns a manifest file and decompiling C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll (5171KB) returns a manifest and all types in the assembly.

I cannot find any information on why the assemblies are built in such a way.

What are the differences in the two assembly directories and why have two copies on the filesystem? Why are types duplicated in both assemblies? Both System.Runtime and mscorlib contain most of the same types.

Was it helpful?

Solution

The assemblies you found in C:\Program Files (x86)\Reference Assemblies are reference assemblies. They are rather special in .NET 4.0 and up, they don't contain any code, just the type declarations. A compiler only uses the metadata in such an assembly to compile your code. At runtime you get a very different assembly, it is retrieved from the GAC.

Do note that you'll find many copies of System.Runtime.dll in that directory, the .NETPortable directory in particular has lots of profiles each with their own copy of that reference assembly. With different sets of types, the ones that are appropriate for that particular profile.

The assemblies you found in C:\Windows\Microsoft.NET\Framework\v4.0.30319 are a copy of the ones in the GAC. Whatever version of the framework you actually have installed. You should never use these assemblies for anything. While many assemblies there still have an [AssemblyVersion("4.0.0.0")], their content is dramatically different, particularly between 4.0 and 4.5. You can see this back in the documentation, the ExtensionAttribute class is a good example. In .NET 4.0 it lives in System.Core.dll, in 4.5 and up it now lives in mscorlib.dll. It would be better if these copies were not be around anymore, unfortunately System.CodeDom, sgen.exe and legacy tooling depends on them being there. Using them as references can be very troublesome when the program runs on another machine with a different framework version installed.

So look at the assemblies in the GAC to know what really happens at runtime. A big change there as well since .NET 4.0, it now lives in another directory. Previously in c:\windows\assembly, now in c:\windows\microsoft.net\assembly. And the most visible change, it no longer has the shell extension that stopped you from navigating to files in that directory. You can directly navigate the GAC folder structures. It is a wee bit convoluted since assemblies that contain unmanaged code (like mscorlib.dll) are stored in a separate directory. Have a look-see, you'll have little trouble figuring out the scheme.

The C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll assembly you'll find there is indeed rather empty. You probably missed the most important detail in the manifest however. It contains a lot of [TypeForwardedTo] attributes. A snip of the ones you'll find there:

[assembly: TypeForwardedTo(typeof(Action))]
[assembly: TypeForwardedTo(typeof(Action<>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,,>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,,,>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,,,,>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,,,,,>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,,,,,,>))]
[assembly: TypeForwardedTo(typeof(Action<,,,,,,,,,,,,,,,>))]
// etc, many more

Maybe you can see what's going on now, System.Runtime.dll does not contain any code at all. It is an adapter that forwards types from one assembly to another. The desktop version of .NET forwards types to mscorlib.dll, System.dll, System.ComponentModel.Composition and System.Core.

"The desktop version" in the previous sentence is the key that explains why this was done. There are many .NET Framework versions, they have different forwarders in System.Runtime. These adapter assemblies buys Microsoft an extra level of indirection. It helps you to write .NET code that is platform agnostic and runs the same without changes whether you execute it on the desktop, a Store app, in the browser with Silverlight, on the XBox game console, on a phone. Even though the latter ones have a rather dramatically different framework, a much smaller one named .NETCore. The Portable Class Library project template is the principal beneficiary.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top