In the .NET framework 3.5, you have a lot of functionality for writing your custom plug-ins of add-ins for your own application. In the .NET framework 2.0 you have not. I was searching a time on the Internet to find out how to do this. Finally I got something, but the description was not completely clear to me, so I figured out how to deal with this. In this post, I wrote in detail how to get your (host) application plug-in enabled. The sample enclosed is written on Visual Studio 2008
The idea is that you have your host application, you plug-in dll and in between an interface. I started with a simple host application, called ThePluginDemo.exe.This application has one form with a menu and a status bar on it. The plugin we will create, add a new menu option called "My Plugins" with a menu item under it called "Plugin Demo". Ok, let's start.
1. Pre-configuration of host application
On the "My Project"details, the root namespace is edited to PluginDemo.Presentation and the Output Path is changed to ..\Output\.
Note: All the projects in this solution will have the same output path, so you don't have to copy/paste the new dll's and stuff when you did a rebuild action.
2. Creating the interface
1. Add a new project to the solution:
An Empty Project was selected, the .NET Framework 2.0 option was selected in the drop down box on the top right corner of the window, and the project was called PluginInterfaces
2. On the "My Project" details , the Root namespace is changed to PluginDemo.Interfaces, the application type is changed to Class Library and also the Output Path is changed to ..\Output\.
3. A new VB-class file was added and the default code was replaced to:
Imports System Imports System.Windows.Forms Public Interface IPlugin Sub AddMenuItems(ByVal target As MenuStrip) End Interface
The sub AddMenuItems is the procedure that will be called later on to add the new menu item of the plugin.
Don't forget to add a reference to system.windows.forms.
4. On the project ThePluginDemo (= host application), add a reference to PluginInterfaces.
3. Create the Plug-in project
1. A new, empty project (on .NET framework 2.0) was added to the solution with the name MyPlugin, Root namespace PluginDemo.Plugins, Output Path ..\Output\.
2. References to PluginInterfaces and system.windows.forms were added.
3. A new VB-class file was added, called MyFirstPlugin. The code of the file was edited so it looks like:
Imports PluginDemo.PluginInterfaces Imports System.Windows.Forms Public Class MyFirstPlugin Implements IPlugin Public Sub AddMenuItems( _ ByVal target As MenuStrip) _ Implements _ PluginInterfaces.IPlugin.AddMenuItems End Sub End Class
A procedure was added which will be called when pressing on the menu item created by this plugin:
Public Sub miPluginDemo_Clicked( _ ByVal sender As Object, _ ByVal e As EventArgs) MessageBox.Show("Plugin Demo menu item clicked!") End Sub
Note: the parameters sender and e are needed, otherwise the method does not have a signature compatible with delegate 'Delegate Sub EventHandler(...)'.
4. The following code was added to this class and is responsible for adding new menu options:
Private mainMenu As MenuStrip Public Sub AddMenuItems( _ ByVal target As MenuStrip) _ Implements PluginInterfaces.IPlugin.AddMenuItems ' Store target menu strip in a local variable mainMenu = target ' Check if the report plugin menu item already exists If Not mainMenu.Items.ContainsKey("miMyPluginDemo") Then ' Create the report plugin menu item Dim reportMenu As New ToolStripMenuItem("My Plugins") reportMenu.Name = "miMyPluginDemo" mainMenu.Items.Add(reportMenu) End If ' Create a ToolStripMenuItem as submenu item with an event Dim mi As New ToolStripMenuItem mi.Name = "miPluginDemo" mi.Text = "Plugin Demo" ' Attach a handler that fires a procedure AddHandler (mi.Click), AddressOf miPluginDemo_Clicked ' Add the toolstripmenuitem to the mainmenu/report AddMenuItemToReportMenu(mi) End Sub ' Procedure to add a ToolStripMenuItem to the mainmenu/report ' location. Private Sub AddMenuItemToReportMenu( _ ByVal mi As ToolStripMenuItem) Dim tsmi As ToolStripMenuItem tsmi = CType(mainMenu.Items.Item("miMyPluginDemo") _ , ToolStripMenuItem) tsmi.DropDownItems.Add(mi) End Sub
4. Create a plug-in XML file
To load the plugin dynamically, a XML file will be created that lists the available plugins. In this example, just one plugin dll.
1. A new XML file was added to the MyPlugin project and is called PluginList.xml
2. The content was edited, so it looks like:
1 PluginDemo.Plugins.MyFirstPlugin MyPlugin
ID: a sequence number and should be unique when you have more than one plugin you want to use.
Type: the namespace of the MyPlugin project followed by the class name that contains the plugin functionality you want to use.
File: the name of the file created by building the MyPlugin project (without extension).
3. The file properties of this XML file were edited to:
Build action = None; Copy to Output Directory = Copy Always
5. Load plugin in host application
Finally, we edit the host application so it will load the plugin dynamically.
1. Imports to the PluginInterface and to system.reflection were added:
Imports PluginDemo.PluginInterfaces Imports System.Reflection
2. A new dataset object was added with filename Plugins.xsd.
A DataTable was added and named PluginTool (see also the tag in the xml file)
Three columns were added:
* Name: ID; DataType: System.Int32; Unique: True
* Name: Type; DataType: System.String
* Name: File; DataType: System.String
3. On the mainform, two private variable were declared to hold the content of the XML document as created in step 4.
Private pluginXml As Plugins Private pluginList As ArrayList
4. Finally, a procedure was added to load the plugin(s). The call to this procedure was placed in the form onload event.
Private Sub LoadPlugins() ' Create a new instance of the plugins pluginXml = New Plugins Try ' Try to load the XML file with the ' available(plugin(s)) information pluginXml.ReadXml("PluginList.xml") ' Create a new instance of an ArrayList Me.pluginList = New ArrayList ' Step through all available PluginTools, ' read from the XML file and add them to ' the(ArrayList) Dim r As Plugins.PluginToolRow For Each r In pluginXml.PluginTool Dim a As Assembly = Assembly.Load(r.File) Dim t As Type = a.GetType(r.Type) Dim p As IPlugin = _ CType(Activator.CreateInstance(t) _ , IPlugin) Me.pluginList.Add(p) Next For Each o As Object In Me.pluginList Dim p As IPlugin = DirectCast(o, IPlugin) p.AddMenuItems(Me.MainMenu) Next ' Show statusbar message when plugin(s) ' is (are) (succesfully) loaded tsStatus1.Text = "Plugin(s) loaded." Catch ex As Exception ' Show statusbar message when loading ' plugin(s) failed tsStatus1.Text = "Plugin(s) not loaded." End Try End Sub
5. This is it! We are done. Running the application shows the plugin loaded.
The complete solution can you download here: ThePluginDemo.zip.