Search code examples
c#.netclr.net-assemblyassemblyresolve

Dependent assembly resolution succeeds even with the wrong version number


I've been searching for the answer to the strange behavior I detected in assembly resolution to no avails. I am aware that the CLR records an assembly's references into its metadata(manifest). It records the name, version number, public key token and locale. Upon the load of the assembly each referenced assembly is probed and loaded. This probe is version-sensitive or in other words the same version used in build, should be located and loaded, not any other version. If the assembly is signed, public key also comes into play.

The problem is that in a dummy application I created for testing, this rule is broken! I have already searched SO and google and answers are off the mark. Please do not give me the following reasons, workarounds:

  • "Specific Version" is set to False: this is only true when in compile time and doesn't have anything to do with runtime.
  • Also, there is no or application/machine configuration set.

In my testing setup I have Project A referencing Project B. After versioning each I change project B and only build itself and not A. Now I copy the new B.DLL with the changed version into the A's working directory and run A. It works !! I expect it to crash.

The Fuselogvw.exe's output should be self explanatory. In the log it is mentioned that the assembly should look for version 9 but version 8 is located and loaded ! Notice the line:

LOG: Assembly Name is: dllProj, Version=1.1.10.8, Culture=neutral, PublicKeyToken=null

*** Assembly Binder Log Entry  (10/8/2014 @ 2:34:51 PM) ***

The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll
Running under executable  C:\...\Documents\Visual Studio 2013\Projects\test1\test1\bin\Release\test1.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = ...
LOG: DisplayName = dllProj, Version=1.1.10.9, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/.../Documents/Visual Studio 2013/Projects/test1/test1/bin/Release/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = NULL
Calling assembly : test1, Version=1.1.1.1, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\...\Documents\Visual Studio 2013\Projects\test1\test1\bin\Release\test1.exe.Config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/.../Documents/Visual Studio 2013/Projects/test1/test1/bin/Release/dllProj.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\...\Documents\Visual Studio 2013\Projects\test1\test1\bin\Release\dllProj.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: dllProj, Version=1.1.10.8, Culture=neutral, PublicKeyToken=null
LOG: Binding succeeds. Returns assembly from C:\...\Documents\Visual Studio 2013\Projects\test1\test1\bin\Release\dllProj.dll.
LOG: Assembly is loaded in default load context.

Solution

  • In the MSDN, there is a fine print note almost at the end of the main page about assembly binding:

    There is no version checking for assemblies without strong names, nor does the runtime check in the global assembly cache for assemblies without strong names.

    There are many things that can affect assembly binding, but in this particular example of yours, this behavior is defined by the fact that the B assembly is not being referenced using a strong name.