I wrote this method to resolve assemblies. It is tweaked to fit my needs.
It basically hooks a AssemblyResolve
event to the current application domain to retrieve an requested assembly from a list of directories.
There is no easy way to find where the assembly file that match the namespace to resolve, except by loading an assembly file and check to which namespace it belongs to.
Plus, it discards some unwanted assemblies (like serializers, resources...) and detects dlls or exes that are not .NET assemblies.
A better approach would consist in using the Global Assembly Cache, but we want our plugins to be fully moveable. So here it is.
public static class AssemblyResolver
{
internal static void Hook(params string[] folders)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
// Check if the requested assembly is part of the loaded assemblies
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (loadedAssembly != null)
return loadedAssembly;
// This resolver is called when an loaded control tries to load a generated XmlSerializer - We need to discard it.
// http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization
var n = new AssemblyName(args.Name);
if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase))
return null;
// http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl
if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
return null;
string assy = null;
// Find the corresponding assembly file
foreach (var dir in folders)
{
assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(dir, g)).FirstOrDefault(f =>
{
try { return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name, StringComparison.OrdinalIgnoreCase); }
catch (BadImageFormatException) { return false; /* Bypass assembly is not a .net exe */ }
catch (Exception ex) { throw new ApplicationException("Error loading assembly " + f, ex); }
});
if (assy != null)
return Assembly.LoadFrom(assy);
}
throw new ApplicationException("Assembly " + args.Name + " not found");
};
}
}
Here is how it works:
AssemblyResolver.Hook("Plugins", "CommonReferences");
Everytime some assemblies needs to be resolved, it will get the one that is loaded in memory, otherwise it will search in any given folders.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…