Friday, October 29, 2010

The Mystery of mscorlib

I had an interesting problem recently while converting a Visual Studio 2008 project to Visual Studio 2010. This was a solution that included a website project and a number of library projects (side note: I don't recommend website projects, try web application projects instead). The website project and all of the library projects targeted the .NET 3.5 framework. The Reason I was converting this project was just to use the new features of VS 2010; I didn't want to go through a full conversion of the project to .NET 4.0 because that would have meant testing and deployment time I didn't have. So when the framework warning dialog appeared:


I clicked, "No." So far so good.

But now I had a problem. When I rebuilt the project, I saw the following error:

The primary reference could not be resolved because it has an indirect dependency on the .NET Framework assembly "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" which has a higher version "4.0.0.0" than the version "2.0.0.0" in the current target framework.

Huh? I understand that it's not a good idea to mix 2.0 and 4.0 assemblies, but I hadn't asked for any links to the 4.0 runtime. The reference to version 2.0 is confusing, until you reflect on the fact that a project targeting 3.5 is actually using version 2.0 of the core framework. That's why it's so easy to upgrade project from 2.0 to 3.5. But where did the .NET 4.0 reference come from? A little more investigation revealed that reference was embedded in just 1 of the library projects. I was able to build this project successfully, but when I did I got some strange errors, such as:

Warning 5 The predefined type 'System.Action' is defined in multiple assemblies in the global alias; using definition from 'c:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll'

So it looked like the 4.0 library was being used, but why, and why wasn't I able to see this anywhere in the project configuration information? The property pages for the project showed that it was targeting framework version 3.5. When I looked at the assembly references for the project, I couldn't see any reference to mscorlib, either 2.0 or 4.0:


This got me thinking: I knew that mscorlib is the assembly that contains the core implementation of the .NET framework, so why don't we see it in the Visual Studio reference list? Apparently the VS team thinks it's none of our business. Fortunately we don't have to be limited by this restriction - I loaded up Redgate Reflector and found the complete list of references for this assembly:


Now we're getting somewhere! We can see both mscorlib references and we can see that one reference is to the 4.0 version of .NET. Comparing this display to the Visual Studio version, we can see that VS represents the mscorlib assembly with logical references that don't exactly match the assembly references. Is this a good thing? I would say no. My view is: when you are looking at the reference list, you really are looking for assemblies, not logical namespaces. Also, obscuring the mscorlib assembly in the reference list is not consistent with the way other assemblies are displayed.

Back to our problem: how do we eliminate this phantom 4.0 reference? For the solution I relied on my old friend Subversion version control. I ran a diff on the projects that converted successfully and compared those to a diff on the project with the .NET 4.0 reference. It turned out that the library with the .NET 4.0 reference had been completely skipped during the upgrade process for some reason. I was able to fix the problem by merging the diffs from the successfully converted files into the problem project by hand. I found that the key problem was at the top of the project file. I replaced this heading:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="2.0">

with this heading:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">

And added this section to the PropertyGroup element:

<oldtoolsversion>2.0</oldtoolsversion>
<upgradebackuplocation>
</upgradebackuplocation>

There were also some discrepancies with assembly references, but I found that when I merged the entries shown above the assembly references took care of themselves with a rebuild, and the rogue reference to mscorlib version 4.0 had been purged.

So I learned something about mscorlib, and as is so often the case the learning experience involved some frustration. The account in this blog post omits many of the detours I took while investigating this problem. I hope posting my results here I might save others from spending too much time on this. Remember: ignore mscorlib at your peril!

4 comments:

Unknown said...

Thanks so much for providing this solution. I have just upgraded a large set of projects from vs2008 to vs2010 and the spurious reference to mscorlib v4.0 was driving me nuts! Any dependent project that had .Net 2.0 output stopped compiling, because vs2010 incorrectly claimed that the referred projects targeted .Net 4.0.

Best regards,

Berend Engelbrecht

Paul Keister said...

Berend, it's good to know this was helpful, thanks!

Unknown said...

I'm late to the party on this one, but I found a different cause for this so thought I'd throw it out. After ripping out some hair I finally found my cause. I was looking at what it would take to switch out my code base from 3.5 to 4.0. It proved to cause a lot of problems so I switched back. Then it happened ...

Turned out there is a directory for compiled MSIL in the framework\assembly directory and it had a bunch of listings for my project outputs. Once I deleted those my issue went away.

Hope this helps some of you.

Milox said...

man I think I love you, this is valuable info!