How to dynamically load an assembly from custom directory including its dependencies?

3

I found the following code that loads a dll dynamically from a custom location:

private void Form1_Load(object sender, EventArgs e)
{
    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}

private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
    string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    string assemblyPath = Path.Combine(folderPath, "libs", new AssemblyName(args.Name).Name + ".dll");
    if (File.Exists(assemblyPath) == false) return null;
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    return assembly;
}

private void button1_Click(object sender, EventArgs e)
{
    var zip = ZipFile.Read("test.zip");

    foreach (ZipEntry file in zip)
    {
        file.Extract(".", ExtractExistingFileAction.OverwriteSilently);
    }
}

In some cases this solution works, but with the ZipDotNet dll I'm getting:

InnerException  {"File or assembly name \"Ionic.Zip, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c\" or one of its dependencies, was not found. Operation is not supported. (Exception from HRESULT: 0x80131515)":"Ionic.Zip, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c"}   System.Exception {System.IO.FileLoadException}


Since its passing if (File.Exists(assemblyPath) == false) return null; I' guessing its a problem with loading the depencies of Ionic.Zip.dll? How would I resolve them, too?

c#
.net
.net-assembly
assembly-resolution
resolve
asked on Stack Overflow Mar 19, 2013 by christophrus • edited Dec 13, 2018 by Cœur

1 Answer

4

Looking at the source code of Iconic.Zip.dll here, I see that the only references in the project are to System, System.Data, System.Security, and System.Xml, so I doubt the reason is that you are unable to load any of its references.

I think your assembly resolve method is a little too simple. It is assuming the assembly name is the same as the file name, which is not always the case. So what you can do is get the AssemblyName for each of the dlls in the target folder and store a mapping of names to files. Then when the resolver requests a name you look it up in your known names and load the assembly file. Here is a quick implementation of the idea.

private Dictionary<string, string> assemblyNameToFileMapping = new Dictionary<string, string>();

private void Form1_Load(object sender, EventArgs e)
{
    GetAssemblyNames();
    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}

private void GetAssemblyNames()
{
  string folderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "libs");
  foreach(string file in Directory.EnumerateFiles(folderPath, "*.dll"))
  {
     try
     {
         AssemblyName name = AssemblyName.GetAssemblyName(file);
         assemblyNameToFileMapping.Add(name.FullName, file);
     }
     catch { } // Just move on if we can't get the name.
  }
}

private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
    string file;
    if (assemblyNameToFileMapping.TryGetValue(args.Name, out file))
    {
       return Assembly.LoadFrom(file);
    }
    return null;
}
answered on Stack Overflow Mar 19, 2013 by Mike Zboray • edited Mar 19, 2013 by Mike Zboray

User contributions licensed under CC BY-SA 3.0