Plug It In

Plug ins. Many applications nowadays support the use of plug ins. It is one way for the users of an application to customise and extend its functionalities without the need to access the underlying source code of the application. Take, for example, the Eclipse IDE which is best known for its great extent of componentization – almost everything in the IDE is a plugin. As someone who loves to bodge up random things, I’m always interested to find out how an application is made to support plug ins.

Plugin Support

Before I move on, the demo code for this article can be found here: GitHub – Simple Plugin Example. It is a Visual Studio solution containing 3 C/C++ projects, 1 for the main application that you can run, while the rest are plugins.

To design an application that supports plug ins, we need well-defined interfaces through which the external plugins and the applications will communicate. The designer of the application has to spell out, with enough detail, what kind of functions it expects from a compatible plugin. Usually, this is done through extensive documentation.

Today, many object-oriented programming languages support something called “reflection” that allows one to extract metadata from compiled source code. In some way, this makes the development of plugin support much easier as the application designer just needs to scan through a given plugin’s metadata and look for compatible interfaces.

Of course, I always like to get my hands dirty, so we will focus more on developing plugins using nothing more than good ol’ C/C++ and building dynamically-linked libraries (DLL) from the ground up.

On Windows, a DLL is a binary file containing reusable machine code, ready to be inserted into a process on runtime. This is usually done through the LoadLibrary function to, as its name suggests, load the DLL into memory of the calling process (in the case of the demo, it is the MainApp). Then to use the functions exported by the library, the GetProcAddress function can be used to obtain the function pointer for a given exported function name. You can see this happening in the following snippet.

HMODULE mod = LoadLibrary(pPlugin->m_fileName);
int success = 1;

if (!mod) 
return 0;

FN_PLUGIN_NAME pfnPluginName;
FN_PLUGIN_VER  pfnPluginVersion;
pfnPluginName = (FN_PLUGIN_NAME) GetProcAddress(mod, "GetSimplePluginName");
pfnPluginVersion = (FN_PLUGIN_VER)GetProcAddress(mod, "GetSimplePluginVersion");

The FN_PLUGIN_NAME and FN_PLUGIN_VER are type definitions for function pointers corresponding to the signatures of GetSimplePluginName and GetSimplePluginVersion. As far as interfaces are concerned, this means that the MainApp expects to see these two signatures being exported by the plugin DLL. Looking at the source code for the SimplePluginA, we can see the corresponding function definitions:

DLL_EXPORT int GetSimplePluginVersion() {
	return PLUGIN_VERSION;
}

DLL_EXPORT void GetSimplePluginName(char* pszOutName, size_t size) {
	static char pluginName[] = PLUGIN_NAME;
	size_t i;
	for (i = 0; i < size && pluginName[i]; i++) 
		pszOutName[i] = pluginName[i];
	pszOutName[i] = 0;
}

There is one more function that the MainApp’s plugin has to export: the Execute function. It is, for all intents and purposes, where the main logic of the plugin will go.

MainApp Design

I think the design of the MainApp and the 2 plugins can be summarised into the following diagram:

Plugin-1

When the MainApp invokes the Execute function in a loaded plugin, it passes into it some function pointers from the MainApp that can be used within the plugin to call for services from the main application.

The way MainApp runs is as follows:

  1. Look for compatible DLL files within the working directory of the MainApp. It does this by iterating through all files that matches *.dll filter and checking to see if the signatures for GetSimplePluginVersion and GetSimplePluginName exist.
  2. The user is prompted to perform one of these operations: (i) Load plugin; (ii) Unload plugin; (iii) Show a list of compatible plugins found; (iv) Execute a loaded plugin; (v) Quit the application (auto-unloads all loaded plugins)

The solution is very simple and it is just a matter of opening it up within Visual Studio 2017 and building it. There are two plugins: one to perform addition of two integers, and one to list out the first N number of Fibonacci sequence numbers (the plugin will prompt the user for the said N).

plugin1
Initial console output, listing all compatible plugins detected, followed by an attempt to load the Addition plugin.
plugin2
Listing the plugins again, this time with the additional Loaded status displayed.
plugin3
Executing the Addition plugin.

You can create more plugins yourself to try it out. If you tinkered around with the two plugin projects, you will realise that the source code includes the plugin.h file from the MainApp project, which defines the ParentInstance interface structure and the required functions to be implemented.

Leave a comment