After installing .NET 4.0 or later you may notice something a little unusual about your .NET processes. Here is a partial list of the loaded modules of a simple “Hello World” executable compiled with the .NET 2.0 compiler.
start end module name
60f00000 61491000 mscorwks C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
6c650000 6c6b6000 mscoreei C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
6d420000 6d46a000 MSCOREE C:\Windows\SYSTEM32\MSCOREE.DLL
75a80000 75aca000 KERNELBASE C:\Windows\system32\KERNELBASE.dll
Something here looks out of place – the mscoreei.dll file is loaded from the v4.0.30319 folder. What is it doing there next to the main clr dll (mscorwks.dll) from v2.0.50727? Actually, that’s the expected behavior. We call mscoreei.dll the “shim implementation”, or “shim impl” for short, and it’s new for .NET 4.0. The third dll in the list above, previously known as the “shim”, is now more accurately known as the “shell shim”. The two are tightly paired to do the main job that was previously done by mscoree alone – providing the interfaces for loading the runtime. Generally speaking, the shell shim now consists of thin wrapper functions, each of which delegates its functionality to a corresponding function in the shim implementation.
Why the split? We’ve seen significant numbers of machine reboots as part of .NET redist installation, typically due to needing to update a file that is in use. The most common file in use has been mscoree.dll, which is loaded by every single .NET application, and even some services such as MSI. As a result, we undertook the mscoree “split” to avoid machine reboots. By moving the shim implementation into a version-specific file, we are able to deploy a new version of mscoreei.dll (say in the v5.0 .NET folder, as part of .NET 5.0 installation), without touching the machine-wide mscoree.dll file. The next time a managed app is run, mscoree will dynamically find the new mscoreei and defer each of its function calls to it. That way we can deploy new versions of the framework on a machine running existing managed applications without requiring a restart.
Note that mscoree always uses the newest mscoreei it can find, but the actual runtime that is loaded is a completely different question. Therefore it shouldn’t be a surprise to see a newer version of mscoreei loaded with older versions of runtime dlls in the same process. In fact, CLR versions have been running with the latest shim for many years now. If I examine file versions when running a .NET 1.1 app on a machine that has both .NET 1.1 and .NET 2.0 installed I can see that I am using a shim newer than the runtime.
start end module name
79000000 79045000 mscoree C:\WINDOWS\system32\mscoree.dll
File version: 2.0.50727.42
791b0000 79412000 mscorwks C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll
File version: 1.1.4322.573
The difference now is that the split of the shim makes it more obvious that the shim version doesn’t always match the runtime version because the version number is right there in the path of the shim impl.
This brings to mind an aphorism from David Wheeler: All problems in computer science can be solved by another level of indirection.