Well, I am in the process of working this out in .NET, but using C#. I will do my best to explain this. First off their are some things you should know about.
1) Interfaces. These are very useful in a plug-in system. What they allow you to do is give plug-in writers a template of what functions they HAVE to use for your plug-in. In most cases plug-in systems need at least 1 base method that the main program can run, so you want that method to be known. When someone writes a plug-in dll, they can Implement the interface, this forces them to code whatever methods you define in the interface. Also in the base program you can detect what classes in a persons plug-in implements the correct interface and skip loading the dll if it does not.
2) Reflection. This is the basis of the plug-in system. Reflection allows you to dynamically load a dll(plug-in) either by filename, partial class name, or full namespace/class name. Using the System.Reflection.Assembly and the File System stuff, you can loop through all files in directory, pass the filename into the .LoadFrom method of the Assembly class, get an array of classes in that assembly using .GetTypes(), (or load 1 specific class using assembly.GetType(classname)), and create an instance of the class. From there, there are all sorts of nifty little things you can do. you can even use the InvokeMember method of the Type class to run a method by passing in a string that is the Sub/function name. So in short look into Reflection, the Assembly class, and the Type class.
3) Finally you would want to know how to use Collections to store instances of your loaded plug-ins to access whenever you need them.
Here is my C# code the loads the dll dynamically. Now it may be in C#, but if you look carefully, you will notice that most of it it just like VB .NET. It has some comments to help explain what does what.
This should at least help you get started. If you need some help understanding how to change something from C# to VB .NET, just ask and I will help you out with that, but most of it should be self explanatory. A couple perhaps not fully understandable is using = Imports in VB, [] brackets are array brackets, so in VB you use (), and variable declarations are like this in C# Type variablename; . That of course in VB would be Dim variablename As Type.
Code:
using System;
using System.Reflection;
namespace PluginLoad
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class VCommPlugInLoad
{
/// <summary>
/// The main entry point for the application.
/// </summary>
/// <args>Assembly_Name ObjectTypeName</args>
[STAThread]
static void Main(string[] args)
{
VCommPlugInLoad me = new VCommPlugInLoad();
me.LoadPlugIn(args[0]);
}
public void LoadPlugIn(string filename)
{
Type[] ObjType = null;
IPlugin plugin;
PlugInCollection plugList = new PlugInCollection();
// load the dll.
try
{
// load it
Assembly asm = null;
asm = Assembly.LoadFrom(filename);
if (asm != null)
{
// create an array of types
ObjType = asm.GetTypes();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "1");
}
try
{
// We need to find all classes that Implement the IPlugin interface
if (ObjType != null)
{
// lets loop through each type in our array of types in the loaded
// dll
foreach(Type pluginImplemented in ObjType)
{
// If the plugin interface is bound to one of the types, then that type
// is a class that implements IPlugin.
if(pluginImplemented.GetInterface("IPlugin") != null)
{
//Console.WriteLine(pluginImplemented.ToString());
plugin = (IPlugin)Activator.CreateInstance(pluginImplemented);
//plugin.Settings();
pluginImplemented.InvokeMember("Settings", BindingFlags.Default | BindingFlags.InvokeMethod, null, plugin, new Object [0]);
// Initialize and add to collection
plugList.AddPlugin(plugin);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "2");
}
}
}
}
As a note ..you can also see how to run a subroutine by using its name as a string...which is a very popular VB6 question..and thus will be for .NET also.
1) I was experimenting with calling a sub routine by using its name as a string. Both ways work.
2) The interface should just be a dll or just a class with the names of functions/subs/properties that the plugin writer must Implement in their plugin. That way your main application will always know what the actual routine names are.
He used a standard Interface (IPlugin) to define the structure of a plugin. Each plugin must implement that interface to be a plugin. You could also make a PluginBase class and have all plugins inherit from it if you want. The PluginCollection is a collectio nof the objects that implement IPlugin (a.k.a A collection of Plugins).
Basically it gives me the ability to provide the 'rules' a third party developer must follow in order to make a plugin for the app and it lets me verify that they indeed did follow those rules before running the plugin.
When i say rules, I mean what properties and methods that a developer MUST have as a minimum so that the main app knows what to call.
That makes sence, as you don't want them not specifying methods that you call, and thus crashing the program.
I am having trouble casting the Activator.CreateInstance() to the plugin variable though
If I specify plugin as an object, at use InvokeMember() to call methods, it works fine, but I'd rather cast it as an IPlugin, so it all just works better.
the original code is exactly the same, with the exception that I took out the plugin collection (to simplify things). I also renamed a few variables, and placed a few more visual information in the console:
try {
// We need to find all classes that Implement the IPlugin interface
if (ObjType != null) {
// lets loop through each type in our array of types in the loaded
// dll
foreach(Type pluginImplemented in ObjType) {
// If the plugin interface is bound to one of the types, then that type
// is a class that implements IPlugin.
if(pluginImplemented.GetInterface("tfxPlugin") != null) {
Console.WriteLine("Loading: " + pluginImplemented.GetInterface("tfxPlugin"));
also, on a related matter, can the plugins call functions in the main application? So things like get_word() or word_count() can be built into the exe and don't have to have to be in each dll. I'll be doing a lot of string minipulation, and i've written a lot of string parsing functions that I'd like to have in the main exe, and available in the plugins
Unhandled Exception: System.IO.FileNotFoundException: File or assembly name Interface, or one of its dependencies, was not found.
File name: "Interface" at PluginLoad.LoadPlugin.Main(String[] args)
try {
// We need to find all classes that Implement the IPlugin interface
if (ObjType != null) {
// lets loop through each type in our array of types in the loaded
// dll
foreach(Type pluginImplemented in ObjType) {
// If the plugin interface is bound to one of the types, then that type
// is a class that implements IPlugin.
if(pluginImplemented.GetInterface("tfxPlugin") != null) {
Console.WriteLine("Loading: " + pluginImplemented.GetInterface("tfxPlugin"));
I think you got something bound up and nutty going on with SharpDevelop. I would suggest creating a new set of projects, and copy and paste the code to the new projects, compile the interface dll, then add it as a reference for the pluigin and exe and try it. Whatever it is , I think it is a quirk with Sharp Deveop