In this post, I will describe an interesting problem that my colleague ran into at work. His original VS solution consisted of two projects targeting .NET 3.5 – let’s name them Alpha and Beta. Alpha had a direct reference to Beta and thus depended on it. Beta was using NHibernate, Castle.Core and some other libraries. When compiling Alpha, he received:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(1360,9): warning MSB3258:
The primary reference "PathToBeta.dll" could not be resolved because it has an indirect dependency
on the .NET Framework assembly "mscorlib, Version=126.96.36.199, Culture=neutral,
PublicKeyToken=b77a5c561934e089" which has a higher version "188.8.131.52"
than the version "184.108.40.206"
in the current target framework.
and the whole build failed with:
Error 3 The name 'Beta' does not exist in the current context
At first sight, we thought that the problem lied within the Beta project configuration and we checked its target framework (v3.5) and platform (
AnyCPU). Everything seemed to be fine so the next step was to enable a detailed MSBuild log and see whether it will tell us more about what was going on. To set the MSBuild build output verbosity, open the Options… dialog in Visual Studio and then choose Build and Run settings:
After a rebuild, we could see that the Csc MSBuild task was missing /reference to the Beta.dll assembly and thus failed at compilation (I dotted unimportant parts):
2> C:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe /noconfig /nowarn:1701,1702,2008
/nostdlib+ /platform:AnyCPU /errorreport:prompt /warn:4 /define:DEBUG;TRACE /errorendlocation
/reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll"
/reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll"
/debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\Alpha.exe /target:exe /utf8output ...
2> Microsoft (R) Visual C# Compiler version 4.0.30319.17929
2> for Microsoft (R) .NET Framework 4.5
2> Copyright (C) Microsoft Corporation. All rights reserved.
2>...\Program.cs(13,31,13,39): error CS0103: The name 'Beta' does not exist in the current context
2> The command exited with code 1.
2>Done executing task "Csc" -- FAILED.
Our next question was why MSBuild did not provide this reference? To answer this, we needed to examine a part of the MSBuild log that described the Beta project compilation process, in particular lines produced by the ResolveAssemblyReferences MSBuild task. For the
Castle.Core library reference resolution, we had the following log:
Dependency "Castle.Core, Version=220.127.116.11, Culture=neutral, PublicKeyToken=407dd0808d44fbdc".
Resolved file path is "...\SharedAssemblies\Castle.Core.dll".
Reference found at search path location "...\SharedAssemblies".
For SearchPath "...\SharedAssemblies".
Considered "...\SharedAssemblies\Castle.Core.exe", but it didn't exist.
Required by "Beta ...".
Found related file "...\SharedAssemblies\Castle.Core.xml".
This reference is not "CopyLocal" because it's registered in the GAC.
The last line is extremely important – it says that the referenced assembly won’t be copied to the output folder because it is registered in the GAC. I checked the GAC on the colleague’s computer and found the following
> gacutil /l Castle.Core
Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.17929
Copyright (c) Microsoft Corporation. All rights reserved.
The Global Assembly Cache contains the following assemblies:
Castle.Core, Version=18.104.22.168, Culture=neutral, PublicKeyToken=407dd0808d44fbdc,
Castle.Core, Version=22.214.171.124, Culture=neutral, PublicKeyToken=407dd0808d44fbdc,
Castle.Core, Version=126.96.36.199, Culture=neutral, PublicKeyToken=407dd0808d44fbdc,
Number of items = 1
Unfortunately, gacutil does not say which versions of .NET those assemblies are targeting so we needed to check this out on our own. If you have .NET2.0/3.5 and .NET4.0/4.5 installed on your machine, then you also have two GAC folders created. Assemblies targeting .NET2.0/3.5 will land in C:\Windows\assembly, while assemblies targeting .NET4.0/4.5 in C:\Windows\Microsoft.NET\assembly. Interestingly, it seems that during assembly resolution, assemblies registered in the .NET4.0/4.5 GAC always take precedence over assemblies with the same version registered in the .NET2.0/3.5 GAC – even if the calling application targets .NET2.0/3.5. Moving back to our case, I found
Castle.Core assembly (v188.8.131.52) in the C:\Windows\Microsoft.NET\assembly folder and run
corflags on it (just to be sure ):
> corflags Castle.Core.dll
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 4.0.30319.17929
Copyright (c) Microsoft Corporation. All rights reserved.
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 0x9
ILONLY : 1
32BITREQ : 0
32BITPREF : 0
Signed : 1
gacutil /u Castle.Core,Version=184.108.40.206,Culture=neutral,PublicKeyToken=407dd0808d44fbdc
resolved all the compilation issues: MSBuild started using Castle.Core.dll from the Shared folder and the Csc task finally received its missing /reference Beta.dll argument .
If you would like to reproduce this issue on your machine (or you are interested in experimenting with GAC), I prepared a sample VS solution that consists of three projects (two libraries:
TestLib2 and a console application:
TestLib2 and all projects target .NET3.5. On the first compilation, everything should work just fine. Now, change the
TestLib2 Target Framework (in the project properties dialog) to .NET4.0, compile it (only
TestLib2) and install with gacutil:
..\TestLib2\bin\Debug\> gacutil /i TestLib2.dll
Then switch the
TestLib2 Target Framework back to .NET3.5 and try to compile the solution. You should receive the same MSBuild warning and failed notice as my colleague did. Happy diagnosing.