Everything About Plugins

Environment: VB.NET

About This Article

I want to give people a basic idea of how plugins should work and what kinds of ways they can be made and used. (There will be no exact coding because that is very different in every case.)

What Are Plugins?

Plugins are pieces of code (often ATLs or .NET DLLs) that intergrate with a host application, by being in a certain folder or being registrated, and has (partial) control of the execution of the host program.

A very good example of a program that uses plugins is the Microsoft development environment. VB.NET, C++, Crystal Reports, and maybe other programs installed into that environment are basicly plugins. They are loaded when the environment is started and then they can do their thing (adding menu items, adding project types, loading itself...). MDE (Microsoft development environment) is a good example of a plugin-based application. When you should remove all the plugins (Crystal Reports, VB.NET...) and start it, it would just show an empty form.

Not all plugins are meant to change the whole host that much as in MDE. Sometimes, plugins are just for simple routines. For example, take milkshape3d. Milkshape3d is a 3d model editor; milkshape3d plugins can add new "save as.. filetypes".

What Are the Advantages of Plugins?

  • They are very portable and easy to deploy. You have to put a DLL in the plugin folder, or register it with a .reg file.
  • They are small. Plugins are very small, but can pack enormous power.
  • And more, but that depends on the plugin framework that is used!

Making Your Own Plugin Framework

Making a plugin framework isn't hard. It is very easy, when you do it right. Before you can make it, you must make a plan for it. What has it got to do, what do I want it to let do... These are the plugin framework types you can consider:

  • A small plugin framework that lets plugins add one or a few new options. For example, new menu items in the 'export as' menu.
  • Medium scale plugin framework that lets the plugins add features to the host application, such as new forms, new menu items, and changing the host.
  • Big scale plugin framework. This has to be totaly plugin based, which means that the host just loads the plugins, and then lets the plugins do what they have to do.

There is a big difference. Making a small plugin framework that just adds a new option in, for example, a menu is not hard to program, but it keeps plugins very limited. It also requires much less coding, and there aren't many things to go wrong.

Small Plugin Framework

Here is an example of a small plugin framework (note that I left big gaps; that's because it is meant as an example, written in VB 6).

'code for the mainform of an application

sub LoadPlugins

  'this will be the sub launched when the plugins have to load
  dim plugins as new collection
  plugins = getdllsfromfolder (app.path + "\plugins")
  'I won't write down this function because it has nothing to do
  'with plugins
  dim plugp as string
  dim plug as object
  for each plugp in plugins

    set plug = createobject (getbasename(plugp) + ".pluginmain")
    'creates an instance of the pluginmain class in the plugin dll.
    plug.addmenuitems (me)  'will let the plugin be able to run the
                            'addmenu item sub in this form by
                            'passing the object reference

    next
end sub

sub addmenu(caption as string, key as string)

  'this sub is run by a plugin when it wants to create a menu.
  redim preserve MNUPlugins(ubound(MNUPlugins)+1)
  set MNUPlugins(ubound(MNUPlugins) = new menuitem
  MNUPlugins.caption = caption
  MNUPlugins.tag = key  'this is the key used to let the plugin
                        'know that the menu item is pressed

end sub

public sub MNUPlugins_click(index as long)

  'This is just a copy of the previous code for getting the
  'plugins. This isn`t a good way to do it
  'The best way is to store it in arrays
dim plugins as new collection
  plugins = getdllsfromfolder (app.path + "\plugins")
  'I won't write down this function because it has nothing to do
  'with plugins
  dim plugp as string
  dim plug as object
  for each plugp in plugins

    set plug = createobject (getbasename(plugp) + ".pluginmain")
    'creates an instance of the pluginmain class in the plugin DLL.

    plug.menuitempressed(MNUPlugins(index).tag)
    'All plugins are warned that a menu item is pressed and the
    'plugins which has made the menu item (which it can identify
    'because it has left a key, which is now returned, can respond

  next

end sub

'----CODE FOR THE PLUGIN DLL's class pluginmain----

public sub addmenuitems(host as object)

  host.addmenu("Some menu caption", "somemnucpt")

end sub

public sub menuitempressed(menukey as string)

  select case menukey

    case "somemnucpt"

      msgbox "pressed"

  end select

end sub

As you can see, that plugin framework does its job, but is very static.

Medium scale plugin framework

Here is the code of a plugin of FL Studio. FL Studio is a program by Micheal Houston and me and may not be copied in any way. (The code is in VB.NET 2003.) As you can see, the plugins have more room, but still can't do everything.

Imports System.Windows.Forms
Public Class myPlugin
Implements FLStudio.IFLStudioPlugin
Private studio As FLStudio.IFLStudio
Private myMenu As MenuItem
Private myTab As TabPage


Public Sub initialize(ByVal FLStudio As FLStudio.IFLStudio) _
       Implements FLStudio.IFLStudioPlugin.initialize
  studio = FLStudio
  myMenu = New MenuItem("Test Plugin")
  studio.addMenu(myMenu)
  myTab = New TabPage("Test Tab")
  studio.addTab(myTab)
End Sub

Public Function pluginName() As String Implements _
       FLStudio.IFLStudioPlugin.pluginName
  Return "myPlugin"
End Function

Public Function description() As String Implements _
       FLStudio.IFLStudioPlugin.description
  Return "An example FLStudio Plugin"
End Function

Public Sub unload() Implements FLStudio.IFLStudioPlugin.unload 
  studio.removeMenu(myMenu)
  studio.removeTab(myTab)
End Sub

Public ReadOnly Property MustLoad() As Boolean _
       Implements FLStudio.IFLStudioPlugin.MustLoad
  Get
    Return False
End Get
End Property

Public ReadOnly Property Invalid() As Boolean _
       Implements FLStudio.IFLStudioPlugin.Invalid
Get
Return False
End Get
End Property

Public Property configuration() As FLStudio.PluginConfiguration _
       Implements FLStudio.IFLStudioPlugin.configuration
Get
End Get
Set(ByVal Value As FLStudio.PluginConfiguration)
End Set
End Property
End Class

There can be as many plugin classes in a dotnet assembly that is put in the plugin folder as you want. They will be recognized as a plugin when they use the IFLStudioPlugin interface. The plugin host's plugin loader isn't included. If you want to take a look at it, e-mail me bws@cncnz.com.

Big Scale Plugin Framework

A really good plugin framework should have the following things:

  • DLL as plugin library. A DLL has to be seen as a plugin library and not as a plugin itself.
  • Plugins must have priority and be overpowering. When there are two plugins with the same key, the one with the higher priority will survive. When building in that system, it will be very easy for plugin coders to even make mods in other plugins. This also makes it unnecessary to delete DLLs if there is a new version of a plugin. Just put the DLL in the plugin folder and the rest is done for you.
  • The host does nothing except load the plugins. All coding has to be done in plugins. By also using plugin overpowering, the application will be very easy to maintain.
  • Plugins should have total control of themselves and all other plugins. In that way, even the plugin loader can be 'modded' by plugins.
  • Plugins should be able to share and change objects. The plugin host should have a storage system for objects that are shared by plugins. These 'shared-objects' can be, for example, forms. These shared objects should also have a priority and be able to be overpowered by other shared-objects.
  • Plugins should be able to communicate with a message system. A plugin has to be able to send a message to all other plugins, where the other plugins can respond to. Or just to send that same message, but only let the plugin which returns the highest priority respond to that event/message.

I have made, with some other programmers, a few plugin frameworks that are based on this principle. The big advantage is flexibility; the disadvantage is the big chance that coders aren't going to use this right, and make it impossible for themselves.

The first one I made was in Visual Basic 6.0. It uses ActiveX DLLs as plugins. You can find the code, and download here.

The second one I made is made in Visual Basic.NET, and uses .NET assemblies as plugins. I will only give the interfaces used by the pluginframework host and of the plugins:

This code is copyrighted

'plugin interfaces, and classes used as interface.
'this is almost complete and shouldn't change in later versions

Public Interface UMSPlugin
  'info part
  Function getinfo() As Collection
  Sub setinfo(ByVal info As Collection)

  'registration part and unload part
  Sub registerplugin(ByVal host As UMSHost)
  Sub unloadplugin()

  'interaction part
  Function msgpriority(ByVal key As String) As Long
  Sub message(ByVal key As String, Optional ByVal _
              parameters As Collection = Nothing)
End Interface

Public Interface UMSHost
  'info part
  Function getinfo() As Collection

  'plugins
  Overloads Sub AddPlugin(ByVal PluginInterface As UMSPlugin)
  Overloads Sub AddPlugin(ByVal Path As String, Optional ByVal _
            PluginKey As String = "*")
  Overloads Sub AddPlugin(ByVal key As String, ByVal _
            PluginInstance As Object, Optional ByVal priority _
            As Long = 0)
  Function GetPlugin(ByVal key As String) As UMSPlugin
  Function GetPlugins() As Collection
  Sub RemovePlugin(ByVal key As String)

  'shared objects
  Overloads Sub AddSharedObject(ByVal obj As Object,_
                                ByVal key As String, _
                                Optional ByVal priority _
                                As Long = 0, _
                                Optional ByVal extrainfo As _
                                Collection = Nothing)
  Overloads Sub AddSharedObject(ByVal sharedobject As SharedObject)
  Function GetSharedObject_Wrapper(ByVal key As String) _
           As SharedObject
  Function GetSharedObject(ByVal key As String) As Object
  Function GetSharedObjects() As SharedObject()
  Sub RemoveSharedObject(ByVal key As String)

  'Global values
  Overloads Sub AddGlobalValue(ByVal GlobalValue As GlobalValue)
  Overloads Sub AddGlobalValue(ByVal key As String, _
                               ByVal value As Object, _
                               Optional ByVal priority As Long = 0)
  Function GetGlobalValue(ByVal key As String) As GlobalValue
  Sub RemoveGlobalValue(ByVal key As String)


  'plugin communication
  Sub Message(ByVal key As String, _
              Optional ByVal parameters As Collection = Nothing, _
              Optional ByVal prioritybound As Boolean = False)
End Interface

Public Class SharedObject
  'info
  Public key As String
  Public priority As Long

  'additional, for extra info about this shared object
  Public info As Collection

  'object itself
  Public obj As Object
End Class

Public Class GlobalValue
  'info
  Public key As String
  Public priority As Long
  Public value As Object
End Class

A few other programmers and I are going to make a Universal Modding Studio with this plugin framework.

How Should a Plugin Work?

Here are some basic principles about how a plugin should work:

  • Use others' plugin resources. Don't rewrite a whole, for example, INI parser hardcoded in a plugin. Use the existing one or make a new plugin for it. This will make all plugins use one parser which makes the plugins easier to maintain.
  • Never depend to much on a plugin. Never try to depend to much on another plugin because that plugin can change in newer versions. Rather, try to use interfaces.

Links:

http://cncsdk.cncnz.com
http://www.bws.lancerplanet.com/apf



Comments

  • Next part will come soon

    Posted by Legacy on 10/11/2003 12:00am

    Originally posted by: bws

    a next part will come soon

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Hundreds of millions of users have adopted public cloud storage solutions to satisfy their Private Online File Sharing and Collaboration (OFS) needs. With new headlines on cloud privacy issues appearing almost daily, the need to explore private alternatives has never been stronger. Join ESG Senior Analyst Terri McClure and Connected Data in this on-demand webinar to take a look at the business drivers behind OFS adoption, how organizations can benefit from on-premise deployments, and emerging private OFS …

Most Popular Programming Stories

More for Developers

RSS Feeds