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");
    }
}
Kick It on DotNetKicks.com
Share

, , ,

  • Anonymous

    Excellent article! One point re. tip 3: classes don’t need to be made static to have a static constructor.

    • http://www.aboutmycode.com/ Giorgi Dalakishvili

      Thanks for your comment. You are right, static constructor does not require the class to be static.

More in AssemblyResolve, c#, jit
How to Get Elevated Process Path in .Net

As you know retrieving process path in .Net is quite straightforward. For example if you have process instance someProcess you...

Bugs in System.Diagnostics.Process Class

While working on an application which makes use of Process class I found several bugs which occur in special conditions....