AppDomain.AssemblyResolve Event Tips

AppDomain.AssemblyResolve event can be used to load referenced assemblies at runtime. This can be useful in multiple scenarios: you can use it to load a library from a different location or load a library based on bitness or load a library which is embedded in the executable file. In all cases there are some tips and tricks which can help you to easily implement the event handler and avoid exceptions.

Tip 1: Use AssemblyName class to parse the name of the assembly

When the AssemblyResolve event is fired you can use the Name property of ResolveEventArgs class to find the name of the requested assembly. However, it will also include version, culture and public key token of the assembly. Instead of manipulating the string to extract the name part it is much more easier to construct a new instance of AssemblyName class and use the Name property which returns only the name of the library.

private Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
    //args.Name looks like this:
    //MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
    //Don't parse it yourself
    string name = args.Name.Split(',')[0];

    //Use the AssemblyName class to get the name
    name = new AssemblyName(args.Name).Name;
}

Tip 2: AssemblyResolve firing multiple times.

AssemblyResolve event can fire multiple times for the same assembly. Instead of loading it again you should return previously loaded assembly. To keep track of the assemblies you have loaded you can use a dictionary or you can get the loaded assemblies by calling AppDomain.GetAssemblies method. Make sure that you use appropriate locking mechanism to prevent race conditions.

Tip 3: AssemblyResolve and FileNotFoundException.

Even if you are loading the assembly in the AssemblyResolve event handler you might still get a FileNotFoundException if you are not careful. For example the following code will generate the exception (mainclass is defined in the library we are loading) :

static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
    var mainClass = new MainClass();
    mainClass.Print();
}

static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
    return Assembly.LoadFile(@"path to the library");
}

The exception is occurring because jit tries to resolve all assemblies as soon as it hits the Main method. At that point the AssemblyResolve event does not have any subscribers so the event is not raised at all. On way to solve the problem is to move the code which needs the assembly to a separate method:

static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
}
static void Print()
{
    var mainClass = new MainClass();
    mainClass.Print();
}

static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
    return Assembly.LoadFile(@"path to the library");
}

Another solution is to subscribe to the event before the Main method is hit. For example, you can change the Program class to a static class and put the assignment in static constructor.

static class Program
{
    static Program()
    {
        AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
    }
 
    static void Main(string[] args)
    {
        var mainClass = new MainClass();
        mainClass.Print();
    }
 
    static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        return Assembly.LoadFile(@"path to the library");
    }
}
Avatar
Giorgi Dalakishvili
World-Class Software Engineer

Related