Assembly.LoadFrom/LoadFile/Load(byte[]) prefers GAC


Starting in Whidbey beta2, for Assembly.LoadFrom/LoadFile/Load(byte[]), if there is an assembly in GAC with exactly the same assembly identity, we will return the one in GAC.

ReflectionOnlyLoad and ReflectionOnlyLoadFrom APIs are not affected.

C:\>more f:\tools\fullname.cs
using System;
using System.IO;
using System.Reflection;

class MyApp
{
    public static void Main(String[] argv)
    {
        if (argv.Length < 1)
        {
            Console.WriteLine(“Usage: FullName assembly”);
            return;
        }

        String path = Path.GetFullPath(argv[0]);

        try
        {
            Console.WriteLine(“Using Assembly.LoadFrom:”);
            Assembly a = Assembly.LoadFrom(path);
            AssemblyName an = a.GetName();
            Console.WriteLine(“FullName: ” + an.FullName);
            Console.WriteLine(“Location: ” + a.Location);
            Console.WriteLine(“Codebase: ” + a.CodeBase);

            Console.WriteLine();
            Console.WriteLine(“Using Assembly.LoadFile:”);
            a = Assembly.LoadFile(path);
            an = a.GetName();
            Console.WriteLine(“FullName: ” + an.FullName);
            Console.WriteLine(“Location: ” + a.Location);
            Console.WriteLine(“Codebase: ” + a.CodeBase);

            Console.WriteLine();
            Console.WriteLine(“Using Assembly.Load(byte[]):”);
            byte[] bytes = File.ReadAllBytes(path);
            a = Assembly.Load(bytes);
            an = a.GetName();
            Console.WriteLine(“FullName: ” + an.FullName);
            Console.WriteLine(“Location: ” + a.Location);
            Console.WriteLine(“Codebase: ” + a.CodeBase);

            Console.WriteLine();
            Console.WriteLine(“Using Assembly.ReflectionOnlyLoadFrom:”);
            a = Assembly.ReflectionOnlyLoadFrom(path);
            an = a.GetName();
            Console.WriteLine(“FullName: ” + an.FullName);
            Console.WriteLine(“Location: ” + a.Location);
            Console.WriteLine(“Codebase: ” + a.CodeBase);
        }
        catch( Exception e)
        {
            Console.WriteLine(“Exception: {0}”, e.Message);
        }
    }
}

C:\WINDOWS\Microsoft.NET\Framework\v2.0.41101>f:\tools\fullname system.dll
Using Assembly.LoadFrom:
FullName: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Location: C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll
Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll

Using Assembly.LoadFile:
FullName: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Location: C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll
Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll

Using Assembly.Load(byte[]):
FullName: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Location: C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll
Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll

Using Assembly.ReflectionOnlyLoadFrom:
FullName: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Location: C:\WINDOWS\Microsoft.NET\Framework\v2.0.41101\system.dll
Codebase: file:///C:/WINDOWS/Microsoft.NET/Framework/v2.0.41101/system.dll

Comments (12)

  1. Hi Junfeng,

    I notice you can only load 1 assembly with a given strong name using ReflectionOnlyLoad(byte[]). This is diferent to assembly Load(byte[]) where you can load as many as you like. Is this a known issue?

    Am I correct in thinking that assemblies loades using ReflectionOnlyLoad will be garbage collectable? I suspect they can’t be at the moment with the above behaviour.

    Thanks, Jamie.

  2. Why would you want to choose the assembly in the GAC if I already pass the assembly using the Load(byte[]) method?

  3. That is by design.

    Say if you do allow multiple assemblies with the same strong name loaded. Now if you need to resolve a type in the assembly with that strong name, which assembly do you want to use?

    I talked in detail about ReflectionOnlyLoad here

    http://blogs.msdn.com/junfeng/archive/2004/08/24/219691.aspx

  4. Josh,

    I’ll explain the reasoning later in a different blog.

  5. > Now if you need to resolve a type in the

    > assembly with that strong name, which

    > assembly do you want to use?

    >

    Couldn’t the reflect only assembly load context be a first class object? I don’t understand why the reflect only context would be bound to the app domain.

    At the moment I’m using reflect only assemblies in a new app domain. I suspect going back to using Assembly.Load(byte[]) in the original app domain would be quicker.

  6. You are using it to find out if a particular plug-in/addin implements a specific interface, right?

    In CLR AppDomain is the application space. Everyone play in their appdomain. I don’t see this change any time soon.

    Presumeably you use a different appdomain so that you can unload the assembly, right? Why do you think Assembly.Load(byte[]) will be better? It pollutes the original appdomain. How do you want to handle the case you have hundreds of addins? Or you don’t believe you should scale to that level?

    Plugin/Addin sounds like hot topic these days. Maybe I should start talking about it.

  7. Thank you for finally doing this!

    I was using LoadWithPartialName to do in v1.x.

  8. Very interesting that you like it. I am so afraid that people doing private drop will yield at us crazy. I still believe this will happen. But we do this for good reason.

  9. > You are using it to find out if a particular plug-in/addin

    > implements a specific interface, right?

    >

    I’m actually using it to find out what assemblies my target assembly references. The configuration of the app domain the target assembly is ultimately gets loaded into depends on the referenced assemblies.

    For example if the target assembly refereces ‘nunit.framework’, I will create an app domain configured for the NUnit test runner. If the target references ‘Microsoft.VisualStudio.QualityTools.UnitTestFramework’, I’ll want to load an app domain configured for the Team System test runner instead.

    > Why do you think Assembly.Load(byte[]) will be better?

    > It pollutes the original appdomain.

    >

    I could have a separate app domain that was responsible for reflecting on assemblies loaded using Load(byte[]). This domain could be recycled after loading a given number of assemblies. At the moment I’m having to recycle the app domain every time I use ReflectionOnlyLoad(byte[]).

    The alternative is loading the meta data tables myself. I’m only interested in any referenced assemblies, so a *relatively* light weight solution could work. Of cause this is a lot more complex and rather defeats the point of having a reflection only API.

  10. Your scenario seems perfectly fit AppDomainManager. Maybe you want to take a look there.

    http://msdn2.microsoft.com/library/System.AppDomainManager.aspx