load any number of dlls for plugin rather than hardcoding the assembly/type.  
Author Message
hazz





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

The following code successully loads into a panel what was previously an .ocx - now a dll.

assembly1 = Assembly.LoadFrom("TrackPanel.dll")
Dim t As Type = assembly1.GetType("TrackPanel.Panel")
Dim c As Control = CType(Activator.CreateInstance(t), Control)
Me.PanelContainer.Controls.Add(c)

What I want to do is load any number of dll's from a specific directory rather than having to hardcode each one given the following ildasm layout. (at least for this particular dll)

namespace TrackPanel
class TrackPanel.Panel
method MyName() ' returns a string

I am thinking that with so many supporting dlls (axhost/interop/etc) required for side by side mechanisms, I really need to use a config file at this point to specify which dll's in a given directory should be loaded. Previously in a vb6 app these were all .ocx's

Assuming I do use the config file for the plugin, should the fully qualified path (namespace/class) also be included in the config file to get to the method which contains the name that will be the label on a tab control on the host form

If there is always a unique method in any assembly called MyName(), how would I code this to iterate through any and all classes within the namespace to find that method - then invoke it

What isn't clear to me yet is how to specify the argument for the Activator.CreateInstance. by trial and error, I found only one class here would work to show up on the host panel container. All other classes in this dll threw an error. I am assuming it is whichever class contains UserControl_Initialize.

Thank you for any ideas. -greg



.NET Development35  
 
 
nobugz





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

Why are you loading these assemblies yourself Just let the CLR take care of it and set references to these assemblies in your project. Such an assembly doesn't get loaded until your code references a class or control in that assembly. Above all, you won't have to reinvent the wheel when the day comes that you have multiple incompatible versions.


 
 
Duncan Woods





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

Here is how I do it. First load all the dlls in a given directory, then iterate through their types to find classes that implement the plug-in interface:

Load dlls:

/// <summary>
/// Load the assemblies at the provided relative path to the currently executing assembly
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static List<Assembly> Load(string path)
{
List<Assembly> plugIns = new List<Assembly>();

try
{
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
try
{
Assembly plugIn = Assembly.LoadFile(dll);
plugIns.Add(plugIn);

log.Info("Successfully loaded plug-in dll '" + dll + "'");
}
catch (Exception e)
{
log.Error("Failed to load plug in dll '" + dll + "' because " + e.Message, e);
}
}
}
catch (Exception e)
{
log.Error("Failed to search for plug in dlls at '" + path + "' because " + e.Message, e);
}

return plugIns;
}


Get plug-in types:

/// <summary>
/// Load all the non-abstract types that are derived from the provided type in the
/// given set of assemblies
/// </summary>
/// <param name="type">The type to find implementations of</param>
/// <param name="assembly">The assembly to search</param>
/// <returns>Named sorted dictionary of types</returns>
private static SortedDictionary<string, Type> GetImplementingTypes(Type type, Assembly assembly)
{
SortedDictionary<string, Type> list = new SortedDictionary<string, Type>();

foreach (Type candidateType in assembly.GetTypes())
{
if (type.IsAssignableFrom(candidateType) &&
!candidateType.IsAbstract)
{
list[candidateType.FullName] = candidateType;
}
}

return list;
}

 
 
hazz





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

I like these big shifts in my understanding nobogz. Thanks !

so for a plugin...that is to say a "get it only when you need it" mechanism, I could just reference it..kind of like being on retainer....and it won't be part of the memory footprint of the app until it is instantiated So I could declare it but only instantiate it when it is needed and then set to it to null if there should be a reason to reclaim memory The garbage collector would grab it as long as it has a reference to it....I don't see any problems there.

hold on...the design requirements for this are that a customer could create a dll, or it could be created in house...and the app will create a tab of a tab control for any dll in a specified directory (the plugin directory) And if a particular client wanted a different flavored dll, they could plop that dll in the plugin directory instead of the rocky road flavor that doesn't work for them anymore. And there could be 1 to n tabs that show up, depending on how many dll's are in the plug in directory.

Thanks for letting me think through this. -g


 
 
hazz





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

duncan...you're introducing something really important and once I understand what  the following is doing, I'll be able to infer the plugin architecture you're using.

foreach (Type candidateType in assembly.GetTypes()) {
if (type.IsAssignableFrom(candidateType) && !candidateType.IsAbstract) {
   list[candidateType.FullName] = candidateType;
}
}

The static Load() returns a generic list of the assemblies and then GetImplementingTypes is passed each of these assemblies that have been loaded within a for next loop What is IsAssignableFrom doing here

Definition for IsAbstract is "Gets a value indicating whether the Type is abstract and must be overridden. "

Definition for IsAssignableFrom is "Determines whether an instance of the current Type can be assigned from an instance of the specified Type. "

Thanks for elaborating on this a wee bit more if you will.   appreciatively.   -greg

 

ps.. I just saw this;

What I do to speed things up is use Assembly.ReflectionOnlyLoad to load them, then I will iterate through all the assemblies that are referenced by the loaded assembly (GetReferencedAssemblies). Since the plugin implements IPlugin, IPlugin is stored in a known assembly (say, plugin.dll). If the loaded assembly references the assembly that contains IPlugin (plugin.dll), then you can go ahead and do a full load of the assembly and then get the exported types, otherwise you can skip it, significantly reducing resource loading.

http://www.gamedev.net/community/forums/topic.asp topic_id=391382&whichpage=1&#2590864

 


 
 
Duncan Woods





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

Yup you have the right idea - load dlls, obtain set of plug-in implementing types.

IsAssignableFrom is a safe way to determine whether a type loaded from an assembly is a plug-in implementation. Other options like IsSubClassOf will not work if your plug-in base is an actual interface and GetInterfaces will clearly not work if your PlugIn base is abstract or a normal object. IsAssignableFrom will be true if for an instance of the given type you can compile: "IPlugIn p = candidate;" which is what really matters.

I also add !IsAbstract because its possible that someone has implemented your IPlugIn base multiple times and created an abstract base for some of their common functionality so we want to filter out types we cannot instantiate.

>> What I do to speed things up is use Assembly.ReflectionOnlyLoad to load them,

>> then I will iterate through all the assemblies that are referenced by the loaded assembly

This is a good idea that would improve my implementation so thanks for pointing it out. I use a dedicated plug-in directory but its possible that large assemblies used by plugins will be put in the same folder and should not be searched.

Thanks,

Duncan


 
 
Glenn Block MSFT





PostPosted: .NET Base Class Library, load any number of dlls for plugin rather than hardcoding the assembly/type. Top

"What I do to speed things up is use Assembly.ReflectionOnlyLoad to load them, then I will iterate through all the assemblies that are referenced by the loaded assembly (GetReferencedAssemblies). Since the plugin implements IPlugin, IPlugin is stored in a known assembly (say, plugin.dll). If the loaded assembly references the assembly that contains IPlugin (plugin.dll), then you can go ahead and do a full load of the assembly and then get the exported types, otherwise you can skip it, significantly reducing resource loading."

You may want to look into using a seperate AppDomain for looking at your assemblies. Using ReflectionOnlyLoad relaxes security constraints, but I don't believe it reduces resources, as once the assembly is loaded, it is not unloaded. Only by using a seperate AppDomain can you load assemblies, reflect through them, find the ones you want, and then unload the AppDomain thus releasing all the resources.