Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need an AssemblyResolve handler for an assembly which is already loaded?

I have two assemblies: App and AddOn. App references AddOn, but CopyLocal is set to false, since AddOn will be loaded dynamically by App.

Here is the code in AddOn:

namespace AddOn
{
    public class AddOnClass
    {
        public static void DoAddOnStuff()
        {
            Console.WriteLine("AddOn is doing stuff.");
        }
    }
}

and here is the code in App:

class Program
{
    static void Main(string[] args)
    {
        Assembly.LoadFrom(@"..\..\..\AddOn\bin\Debug\AddOn.dll");

        // Without this event handler, we get a FileNotFoundException.
        // AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
        // {
        //     return AppDomain.CurrentDomain.GetAssemblies()
        //                     .FirstOrDefault(a => a.FullName == e.Name);
        //};

        CallAddOn();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void CallAddOn()
    {
        AddOnClass.DoAddOnStuff();
    }
}

What I don't understand is why the code doesn't work with the AssemblyResolve handler commented in Main(). When run in Visual Studio, the debugger breaks on CallAddOn() with a FileNotFoundException. Why is it complaining? The assembly is loaded, and it's the exact same version (i.e. same file on disk) as what was referenced by App.

I feel like there is some fundamental concept that I'm not understanding properly here. The commented AssemblyResolve handler works fine, but it seems like a hack and I don't understand why I need it because it seems like it's doing something trivial.

like image 375
RobSiklos Avatar asked Nov 26 '13 02:11

RobSiklos


People also ask

What is AssemblyResolve?

AssemblyResolve event for applications that require greater control over assembly loading. By handling this event, your application can load an assembly into the load context from outside the normal probing paths, select which of several assembly versions to load, emit a dynamic assembly and return it, and so on.

Which event allows you to intervene and manually load an assembly that the CLR Cannot find?

AssemblyResolve event, to handle the attempt to load an assembly when the automatic CLR search has failed.


1 Answers

The reason is that there are multiple assembly loading contexts. The context which an assembly is loaded into affects how it can be used. When an assembly is loaded by the runtime using the default probing mechanism it is put into the so-called Load context. This is the context used when you load an assembly via Assembly.Load. You have loaded the assembly using LoadFrom which uses its own context. Probing does not examine the LoadFrom context and the file is not in the probing path so you are required to resolve it for the runtime. This is not symmetric however. If an assembly is loaded in the Load context, LoadFrom will load it from there first (assuming the identity is the same. for unsigned assemblies, the path is part of the identity.). I will note there are more contexts including ReflectionOnlyLoad and ReflectionOnlyLoadFrom. LoadFile loads an assembly without a context, i.e. all dependencies must be manually loaded.

If you want assemblies to be resolved in the Load context, but have them exist outside the application's default probing path you can do it via configuration as well. Use either the <codebase> element of an assembly binding redirect or the privatePath attribute of the <probing> element.

Read this for more information. There are also some blog posts by Suzanne Cook from a while back on assembly loading and contexts (see here, here, and here).

like image 73
Mike Zboray Avatar answered Oct 14 '22 13:10

Mike Zboray