Understanding Plugins

Author: David Zimmer
Site: http://sandsprite.com

Environment: VB6 All Platforms, Sample plugin Clients in VC++, J++, VB & WSC

Understanding Plugins

So you want to create a plugin framework for your application?

When do you begin?

When I first started doing research on plugins, I found it to be a somewhat hard concept to grasp. How do I let other developers dynamically add functionality to my application without any fore knowledge of who or what they are going to do?

To understand how plugins work is to understand a technique called late binding, and of defining a certain set of rules plugin developers must follow in order to work with your application framework.

To start from the beginning, late binding is the term used to define the act of dynamically creating objects at runtime and then using their methods. When you are using late bound calls, all of your objects will be stored in references of the generic type "Object". If you are working in VB you will not have any IntelliSense available to help you when calling the methods or properties, so you will have to get use to typing somewhat blind.

The following line of Visual Basic Code will dynamically create an object.

dim fso as Object
set fso = CreateObject("Scripting.FileSystemObject")
Scripting.FileSystemObject is the progId.className of a common system component found on most machines. If you were to add a reference to it under the project menu you would find it as "Microsoft Scripting Runtime"

Then you could

dim fso as FileSystemObject
set fso = CreateObject("Scripting.FileSystemObject")
and you would have full IntelliSense for all of its methods.

If you are familiar with Visual Basic, you know that adding a reference is the way that you make use external DLL's. This brings us to understanding just what we need to have a plugin work. Without going into to much detail to confuse you, I will just mention that CreateObject will Create an instance of an ActiveX Component and return its instance to you. For our plugin framework, we will use ActiveX Dll's to house our plugin Clients.

Active X DLL's much like standard projects, except that they can be called and reused by other code and even from other languages. Functions in ActiveX Dlls can only be accessed through its public classes. If you have never created an ActiveX dll, dont worry, its easy and there are samples included.

So to get back to our late binding, early binding example.. Early binding is fine for widely used components that you know you want to use before hand, but does not fit in with our need to load things dynamically at runtime.

Looking at our argument to the CreateObject function, we see that it takes one argument. This argument was further defined as being in the format of progId.ClassName but what exactly does this mean?

In a visual basic DLL project properties, the progId used is the name entered in the "Program Name" field.
This is not necessarily the same as the DLL name! When you go to make plugins you have to be aware of that. The className can be any valid class that you have publicly exposed in your plugin client.

This brings us to our first general rule for our plugin framework. We are going to have to have a method of knowing the progId.Classname for any plugin we encounter without any extra configuration or input from the user so that we can call CreateObject to load it into memory.

How do we do that?

1] Make sure your plugin clients "Program Name" (progId) is the same as the DLL name. - by enumerating which plugin DLL's are in the /plugins directory, we can tell what their progId's if they are the same as their filenames.

2] for the classname, lets use something standard like a class named, well, plugin -although the class name you use is completely arbitrary, it has to be standardized across all of your plugins so that you can call them up without errors and interface with their functions.

Ok, we now know how to dynamically create as many plugins as we can find at runtime, but now how do we integrate with them?

This bring us to the next question, how to these plugins get called?

A typical layout for a plugin framework is to provide a couple menus that the plugins can add entries to. In our framework example, this is exactly what we have done.

When a plugin is first found, first we create the object, now we have a live instance of the plugin class. For us to make use of this plugin class, we have to know what functions to call on it in order to have it register itself with our main program and to call to have it perform the services it has promised us. This brings us to rule

3] The plugin class must have a consistent interface that we can blindly call. It has to be the same for each plugin. Furthermore, it has to let the plugin register its services with the main application, as well as provide a way for the main application to startup any of the functions it promises to provide in that registration.

In our example framework, this is done through a simple plugin class composed of 2 subs.

Sub SetHost(newRef as Object)
sub StartUp(myArg as integer)
After the plugin is first created, the main application will call the SetHost function and pass in a reference to its own main form (or whatever object you want the plugins to have access to)

The plugin will then call a special function we are sure to implement on our main form to register the services it wishes to provide to the host.

In our framework this function in the main application is defined as

Sub RegisterPlugin(intMenu as integer,strMenuName as string, intStartupArg as integer)
Through the object reference passed into the setHost function, the plugin now has a way to call functions on that object. The first one that it calls is the RegisterPlugin sub. In this way, each plugin can supply as many features to the host application as they want. For each one they provide, they supply a different startup argument that will passed back to them when the user selects that entries function.

An example of this would look like the following:

The arguments of the RegsiterPlugin Function are the minimal amount of info needed for the plugin to register itself. The first argument intMenu, allows the plugin to specify which menu it would like this entry to show on. This is provided in case the host allows plugins to add to multiple menus.

The second argument, strMenuNames, is of course the menu text that the plugin want to appear in the new menu item.

The last Argument, is the startUp parameter the plugin expects to receive when this menu item is slected by the user. The reason it passes in a startup argument, is so that each plugin can register multiple functions with its host.

When the plugins functionality is selected, the host will now call the StartUp function in the plugin class and pass back this argument so the plugin can tell which of its functions was chosen.

So now we have a working plugin framework, we have a host that can dynamically load whatever plugins it encounters, and we have a method where the plugin can register as many functions as it wants with the host, and receive its events to start them up at the proper time.

But how do plugins do anything meaningful?

Well, in the SetHost function, remember how we were able to call the RegsiterPlugin function back on the main form? With that reference to frmMain, you can access any of the public variables, functions, and objects that are on that form !

The host developer will commonly provide you with a listing of the public members names and types for your plugin development.

Can I receive events for the controls on the host?

Sure, lets say you wanted to access the onChange event of a textbox on the main form. In your class you could

Dim WithEvents mTextBox as TextBox
then in SetHost

set mTextBox = frmMain.txtMain
(assuming frmMain had a text box named txtMain of course)

If the control you wanted to hook the events for was a type not natively supported in Visual Basic, you would have to add that component to your own library first.

For the last stage of the article, I want to discuss some of the samples I have included in the package. One of the beauties of plugins, is that they use COM. COM, short for Component Object Model, is basically a big plugin framework of its own that lets components written in multiple languages all interoperate seamlessly.

In Visual Basic, COM is an everyday part of life, just about everything we see is a COM object. In other languages such as C++ COM is a very specialized area and not to be taken for granted.

Because COM can work across multiple languages, and because VB Supports COM, you will be quite pleased to find that plugins can be created in just about any language you want!

In this package, I have included plugin samples written in: Visual Basic, C++, & J++. You can even create plugins from scripting languages such as VBScript & .NET

Feel free to use this Generic plugin framework in your own applications. If you do, you are free to distribute these samples with your product to demonstrate how to make plugins for your application in their language of choice.

Now you should have enough background to create your own plugin framework, and understand just how it to make it work for you. Enjoy!

-David Zimmer


Download demo project & Source - 70 Kb