how can i dinamicly load plugins just like i could do in vb6..?
i want a prog that has many plugins and it loads them at runtime...how do i do that?
i tryed the old way and it seemed not to work
Printable View
how can i dinamicly load plugins just like i could do in vb6..?
i want a prog that has many plugins and it loads them at runtime...how do i do that?
i tryed the old way and it seemed not to work
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");
}
}
}
}
yeah tks ill try doin it...and np i know the C# sintax
ahh coolness.:D
Thanks Cander saved me a whole heap of time searching around for how to load a dll at runtime.
Here is the VB.NET translation of Cander's code for those that aren't familiar with the C# syntax:
VB Code:
Imports System Imports System.Reflection 'Summary description for Class1. Class VCommPlugInLoad 'The main entry point for the application. 'Assembly_Name ObjectTypeName Shared Sub Main(ByVal args() As String) Dim m As New VCommPlugInLoad() m.LoadPlugIn(args(0)) End Sub Public Sub LoadPlugIn(ByVal filename As String) Dim ObjType() As Type Dim plugin As IPlugin Dim plugList As New PlugInCollection() 'load the dll. Try 'load it Dim asm As [Assembly] = Nothing asm = [Assembly].LoadFrom(filename) If Not asm Is Nothing Then 'create an array of types ObjType = asm.GetTypes() End If Catch ex As Exception Console.WriteLine(ex.Message + "1") End Try Try 'We need to find all classes that Implement the IPlugin interface If Not ObjType Is Nothing Then 'lets loop through each type in our array of types in the loaded dll Dim pluginImplemented As Type For Each pluginImplemented In ObjType 'If the plugin interface is bound to one of the types, then that type 'is a class that implements IPlugin. If Not pluginImplemented.GetInterface("IPlugin") Is Nothing Then 'Console.WriteLine(pluginImplemented.ToString()); plugin = CType(Activator.CreateInstance(pluginImplemented), IPlugin) 'plugin.Settings(); pluginImplemented.InvokeMember("Settings", BindingFlags.Default + BindingFlags.InvokeMethod, Nothing, plugin, New Object(0)) 'Initialize and add to collection plugList.AddPlugin(plugin) End If Next End If Catch ex As Exception Console.WriteLine(ex.Message + "2") End Try End Sub End Class
Feel free to make any necessary corrections, of course.
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.
look at the InvokeMember method.
:p
I've started this subject in a new thread and Edneeis redirected me to here.
Thanks Cander your useful explanation and Edneeis for your VB .Net translation!
Hi guys! Two simple questions:
1st:
Why do you call:
instead of just:Code:pluginImplemented.InvokeMember("Settings", BindingFlags.Default + BindingFlags.InvokeMethod, Nothing, plugin, New Object())
as I am doing? Was it just an example of an alternative way?Code:plugin.Settings();
2nd:
What is the normal process in deploying an Interface for 3rd parties to develop plugins?
What kind of information is usually given?
Thx!
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.
Just found your code, very nice & exactly what I want to do. Just "two" questions. What are IPlugin & PlugInCollection?
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).
ahh, ok, that makes sence. Thank-you!
Going further on the IPlugin.
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.
Any ideas?
Hmm. what kind of problem?
What kind of changes did you do to the orginal code if any, and what were they?
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:
My DLL is like so:PHP Code:using System;
using System.Reflection;
namespace PluginLoad {
public interface tfxPlugin {
string Author();
string Name();
string Version();
string Info();
bool Initialize();
bool Shutdown();
}
class LoadPlugin {
static void Main(string[] args) {
Type[] ObjType = null;
tfxPlugin plugin;
string filename = "../dll1/dll1.dll";
// load the dll.
try {
// load it
Assembly asm = null;
asm = Assembly.LoadFrom(filename);
if (asm != null) {
// create an array of types
ObjType = asm.GetTypes();
Console.WriteLine("Interogating: " + filename);
}
} 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) {
Console.WriteLine("Found: " + pluginImplemented.ToString());
// 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"));
plugin = (tfxPlugin)Activator.CreateInstance(pluginImplemented);
// put Initialize code here
Console.WriteLine("Loaded: " + plugin.Name());
}
}
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message + "2");
}
}
}
}
PHP Code:// project created on 06/05/2003 at 16:26
using System;
public interface tfxPlugin {
string Author();
string Name();
string Version();
string Info();
bool Initialize();
bool Shutdown();
}
public class DLL1: tfxPlugin
{
public string Author() {
return "Vitani";
}
public string Name() {
return "Test Plugin #1";
}
public string Version() {
return "1.0";
}
public string Info() {
return "This is to test the plugin code";
}
public bool Initialize() {
return true;
}
public bool Shutdown() {
return true;
}
}
But what is the problem?
on the line
plugin = (tfxPlugin)Activator.CreateInstance(pluginImplemented);
I get the error "Specified cast is not valid."
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
Im working on figuring out your problem.
About yopur other question. Put those functions into their own seperate dll. Then you can call it from your main app and the plugins.
ok here we go.
First get rid of the Interface definition in the main executable.
Then, put your Interface definition in its OWN dll.
Remove the interface definition from the plugin.
Then add references to the interface dll in your plugin and exe.
Should work fine.
ok, I've done all that, but now I get this error:
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)
I did exactly what you said. Word for word.
This error occurs before any code is exectuted
You need to re paste all your code again, because you did something wrong.
Main executable:
References tfxInterface.dll
Plugin DLL (dll1.dll):PHP Code:using System;
using System.Reflection;
namespace PluginLoad {
class LoadPlugin {
static void Main(string[] args) {
Type[] ObjType = null;
tfxInterface.tfxPlugin plugin;
string filename = "../dll1/dll1.dll";
// load the dll.
try {
// load it
Assembly asm = null;
asm = Assembly.LoadFrom(filename);
if (asm != null) {
// create an array of types
ObjType = asm.GetTypes();
Console.WriteLine("Interogating: " + filename);
}
} 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) {
Console.WriteLine("Found: " + pluginImplemented.ToString());
// 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"));
plugin = (tfxInterface.tfxPlugin)Activator.CreateInstance(pluginImplemented);
// Initialize
//Console.WriteLine("Loaded '{0}'", pluginImplemented.InvokeMember("Name", BindingFlags.Default | BindingFlags.InvokeMethod, null, plugin, new Object [0]));
Console.WriteLine("Loaded: " + plugin.Name());
}
}
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message + "2");
}
}
}
}
References tfxInterface.dll
Interface DLL (tfxInterface.dll):PHP Code:// project created on 06/05/2003 at 16:26
using System;
public class DLL1: tfxInterface.tfxPlugin
{
public string Author() {
return "Vitani";
}
public string Name() {
return "Test Plugin #1";
}
public string Version() {
return "1.0";
}
public string Info() {
return "This is to test the plugin code";
}
public bool Initialize() {
return true;
}
public bool Shutdown() {
return true;
}
}
All fun & games is this :)PHP Code:public class tfxInterface
{
public interface tfxPlugin {
string Author();
string Name();
string Version();
string Info();
bool Initialize();
bool Shutdown();
}
}
you dont put interfaces IN a class. Change tfxInterface to a namespace
oops!
still crashes, same error :(PHP Code:namespace tfxInterface
{
public interface tfxPlugin {
string Author();
string Name();
string Version();
string Info();
bool Initialize();
bool Shutdown();
}
}
you didnt move the Interface dll after setting references did you?
nope, and I rebuilt all 3 parts when I updatedthe interface
Think you could zip up your project files and attach them to a reply here? Something is not kosher as the above works perfectlly for me.
sorry I took so long to reply, I installed .NET 1.1, rebooted & reinstalled #Dev. Still doesn't work :)
So here it is...
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
ah ha! I put the interface dll in the same folder as the exe, and it worked. Rah, thanks for ALL your help Cander, and for you code.
Pure genius.
ahh ok. Your welcome.
Has anyone made a plugin app for a mobile device (Compact Framework)? I about to give it a try, just wondering if there were any caveats.
Thanks,
Mike
I've not tried it on the CF, but you may need to download extra bits to complament the framework to get it working.
Check out the assemblies at OpenNetCF.org - they add a lot of the full framework's abilities to the compact framework
If you get it working, I'd love to hear about it!
Searching around, there's an excellent article, courtesy of MSDN.
http://msdn.microsoft.com/msdnmag/is...s/default.aspx