views:

77

answers:

3

Here's the setup:

A pure DotNET class library is loaded by an unmanaged desktop application. The Class Library acts as a plugin. This plugin loads little baby plugins of its own (all DotNET Class Libraries), and it does so by reading the dll into memory as a byte-stream, then

Assembly asm = Assembly.Load(COFF_Image);

The problem arises when those little baby plugins have references to other dlls. Since they are loaded via the memory rather than directly from the disk, the framework often cannot find these referenced assemblies and is thus incapable of loading them.

I can add an AssemblyResolver handler to my project and I can see these referenced assemblies drop past. I have a reasonably good idea about where to find these referenced assemblies on the disk, but how can I make sure that the Assmebly I load is the correct one?

In short, how do I reliably go from the System.ResolveEventArgs.Name field to a dll file path, presuming I have a list of all the folders where this dll could be hiding)?

+1  A: 

I've never had luck with AssemblyResolver. I usually do one of these three:

  1. Require plugins not have external references that are not in the GAC. If they bitch, tell them to ILMerge.
  2. Require plugins to dump all their dlls into a known plugin directory. Load all assemblies in that directory into memory.
  3. Require that plugin dependencies exist in a path that is probed by fusion. You can figure out where the binder is looking for assemblies turn on the fusion log (fuslogvw.exe--don't forget to reboot after turning on logging!).
Will
+2  A: 

When I have used this in the past we have just compared the file name with the part of the ResolveEventArgs.Name that has the name. If you want to be sure that you are loading the exact same version I suppose you could check if the names match, if they do then load the assembly and then check the assemblies full name against the ResolveEventArgs.Name.

something along these lines:

string name = GetAssemblyName (args); //gets just the name part of the assembly name
foreach (string searchDirectory in m_searchDirectories)
    {
    string assemblyPath = Path.Combine (executingAssemblyPath, searchDirectory);
    assemblyPath = Path.Combine (assemblyPath, name + ".dll");        
    if (File.Exists (assemblyPath))
        {            
        Assembly assembly = Assembly.LoadFrom (assemblyPath);
        if (assembly.FullName == args.Name)
            return assembly;
        }
    }

for completeness:

private string GetAssemblyName (ResolveEventArgs args)
    {
    String name;
    if (args.Name.IndexOf (",") > -1)
        {
        name = args.Name.Substring (0, args.Name.IndexOf (","));
        }
    else
        {
        name = args.Name;
        }
    return name;
    }
Sam Holder
Sam, isn't this a bit wasteful, in that it wantonly loads assemblies until it finds the right one?
David Rutten
it only loads the dlls that have the same name as the assembly we are trying to resolve. Which when I have done it means it only loads the 1 dll I needed, but if the dll and assembly don't have the same names then you would have to load every dll and check the assembly name against the name you are looking for.
Sam Holder
And I suppose the only way to prevent that from polluting my AppDomain is to create another AppDomain and load them there first?
David Rutten
I would have thought so, yes...
Sam Holder
+2  A: 

The Managed Extensibility Framework (MEF) sounds like something that'll solve all your problems. It can scan folders to locate DLLs, resolve dependencies for any depth and manages plug-in composition in general. Each part (or 'plug-in') just has to declare what it needs and what it provides, and MEF takes care of the wiring. If MEF succeeded in taming VS2010's extensibility beast, then it can handle anything.

Allon Guralnek