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)
{
string name = args.Name.Split(',')[0];
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 (the main class 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. One 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");
}
}