UPDATE 2016-11-11: This issue seems to be fixed (checked on .NET 4.6.1586.0), and a valid version of GAC is used, depending on the framework version the application is using. I will keep the post for reference.
In this post I will describe for you 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. During compilation 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=22.214.171.124, Culture=neutral, PublicKeyToken=b77a5c561934e089" which has a higher version "126.96.36.199" than the version "188.8.131.52" 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 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 the 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>Task "Csc" 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 /preferreduilang:en-US /highentropyva- /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll /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.Data.DataSetExtensions.dll" /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.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> 2> for Microsoft (R) .NET Framework 4.5 2> Copyright (C) Microsoft Corporation. All rights reserved. 2> 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 a following log:
Dependency "Castle.Core, Version=184.108.40.206, 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 my colleague’s computer and found following Castle.Core registrations:
> 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=220.127.116.11, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL Castle.Core, Version=18.104.22.168, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL Castle.Core, Version=22.214.171.124, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL 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 (v126.96.36.199) in the C:\Windows\Microsoft.NET\assembly folder and run corflags on it (just for 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
Uninstalling Castle.Core 188.8.131.52:
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: TestLib1, TestLib2 and a console application: TestApp). TestApp references TestLib1 and TestLib1 references 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. Happy diagnosing:)