12 May, 2010

How to create a plugin framework

I was quite recently faced with the task of creating a “pluggable system” for windows mobile application. Now – this can seem a pretty straightforward thing; but I was actually surprised by the challenges involved therein. Anyway I ended up with quite a good and robust framework. So as a point of reference for myself, the recipe is posted on this blog.

I prefer to adhere to the KISS-principle (Keep It Simple Stupid!) to the extent that makes sense, and the same applies here. Keep it as simple as possible; in this way it usually ends up working and you can always later extend the implementation to satisfy more advanced scenarios if needed. The final Proof of Concept is seen here:

Link to sample project
: Press here

image image

The setup is this:
image

These two interfaces are the main players in the system. The IPlugin is the interface to be implemented by the plugins classes (usercontrols); whereas the IPluginHost is the interface to be implemented by the Host (winforms-application).

IPluginHost
This interface has 2 methods. The RegisterPlugin method registers the plugin into a local collection of available plugins that the host can show. The ShowPlugin method displays the plugin (identified by it’s type) in the hosting form.

IPlugin
This interface has merely 3 simple properties that defines a plugin in this framework. The DisplayTitle is the name to show for the plugin, the Identity is the unique identification of the plugin and the MainInterface is a reference to the usercontrol itself.

Command pattern:
To make the framework a bit more advanced, I in addition want the plugins to be able to interact with the 2 buttons found on the hosting form. To enable this scenario, I have employed the Command pattern.
image

A plugin can decide to implement button support by implementing the IButtonSupport interface. It is a conscious decision from a plugin to implement buttonsupport, not something you are forced to support. Remember that composition is preferred over inheritance according to best practice for software design.

This interface (IButtonSupport) exposes 2 properties (LeftCmd and RightCmd) and a setup method that ties the command to the hosting form (IPluginHost) as the physical buttons are located on this form. The 2 properties LeftCmd and RightCmd are of type ICommand which has a DisplayText and an Execute() method.


Host:
To allow the host to load the plugins, it needs to load them first. This is seen in LoadPlugins() method.

public partial class MainForm : Form, IPluginHost
{
List<IPlugin> m_plugins;
ICommand m_LeftCmd;
ICommand m_RightCmd;

public MainForm()
{
InitializeComponent();

//get plugins
LoadPlugins();
}

void LoadPlugins()
{
m_plugins = new List<IPlugin>();

//get list from finder
var pf = new PluginLoader();
var lst = pf.GetApplicablePlugins();

//register the plugins
foreach (var item in lst)
RegisterPlugin(item);
}

#region IPluginHost Members

public bool RegisterPlugin(IPlugin plugin)
{
//set behavior
plugin.MainInterface.Dock = DockStyle.Fill;

//add to list
m_plugins.Add(plugin);

//add to listbox (UI)
listBox1.DisplayMember = "DisplayTitle";
listBox1.Items.Add(plugin);

return true; //success
}

public void ShowPlugin(Type identity)
{
//find plugin amoun the registared plugins
IPlugin pl = m_plugins.Where(p => p.Identity == identity).FirstOrDefault();
if (pl == null)
return;

lblPTitleValue.Text = pl.DisplayTitle;


//disable buttons (default)
btnLeft.Enabled = false;
btnRight.Enabled = false;
btnLeft.Text = "<text>";
btnRight.Text = "<text>";

//button support?
var bs = pl as IButtonSupport;
if (bs != null)
{
//set host (to allow buttons to work)
bs.SetupButtons(this);

m_LeftCmd = bs.LeftCmd;
m_RightCmd = bs.RightCmd;

btnLeft.Enabled = true;
btnLeft.Text = bs.LeftCmd.ButtonText;

btnRight.Enabled = true;
btnRight.Text = bs.RightCmd.ButtonText;
}

panel1.Controls.Clear();
panel1.Controls.Add(pl.MainInterface);
}

The PluginLoader used by the mainform, is seen here.

namespace Host
{
class PluginLoader
{
/// <summary>
///
Gets the applicable plugins.
/// </summary>
/// <returns></returns>
public List<IPlugin> GetApplicablePlugins()
{
//get files (for now - only identity 'Pl***")
var files = Directory.GetFiles(Application.StartupPath, "Pl*.dll");

var lst = new List<IPlugin>();
foreach (var file in files)
{
var fi = new FileInfo(file);
Debug.Assert(fi != null, "failed to create fileInfo");

var asm = Assembly.LoadFile(fi.FullName);
foreach (var type in asm.GetExportedTypes())
{
if (type.IsPublic) //only look in public types!
{
Type t = type.GetInterface("Contracts.IPlugin"); //search for interface
if(t != null)
lst.Add((IPlugin)Activator.CreateInstance(type));
}
}
}

Debug.Assert(lst.Count > 0, "no plugins found in collection");
return lst;
}
}
}
Plugins:
To allow a plugin to be loadable inside the host, the most basic implementation of IPlugin is seen here:
namespace Plugin2
{
public partial class UserControl1 : UserControl, IPlugin
{
public UserControl1()
{
InitializeComponent();
this.DisplayTitle = "Plugin2.UserControl1";
}

public UserControl MainInterface
{
get { return this; }
}

public Type Identity
{
get { return this.GetType(); }
}

public string DisplayTitle{get;set;}
}
}
If the plugin is also supporting buttonsupport, it needs to implement an additional interface (IButtonSupport) as well.
namespace Plugin1
{
public partial class UserControl1 : UserControl, IPlugin, IButtonSupport
{
ICommand m_left;
ICommand m_right;

public UserControl1()
{
InitializeComponent();
this.DisplayTitle = "Plugin1.UserControl1";
}

public UserControl MainInterface
{
get { return this; }
}

public ICommand LeftCmd
{
get { return m_left; }
}

public ICommand RightCmd
{
get { return m_right; }
}

/// <summary>
///
Sets the host.
/// </summary>
/// <param name="host">
The host.</param>
public void SetupButtons(IPluginHost host)
{
m_left = new ShowCmd(host);
m_right = new NullCmd();
}

public Type Identity
{
get { return this.GetType(); }
}

public string DisplayTitle {get;set;}
}
}
By using this framework, the plugins can do anything as long as they are implemented as UserControls and they are registered in the host. In addition – the plugins can interact with the hosting form without even knowing what they are communicating with. This is the very definition of a loosely coupled design!

03 May, 2010

WCF: how to call asynchronously

It happens that I get a question on how to call webservices in an asynchronous manner. It is actually not that hard, even the tools does not support this out of the box (or so it seems ;-)).

To generate a proxy that supports the async-pattern (Begin<method>/End<method>); you have 2 directions to go:

1) SvcUtil.exe:
You can use the svcutil.exe utility from WCF. The syntax is this:
svcutil.exe /async <url to wsdl-file> (this is the most basic form! More advanced versions exists)

2) VS service reference
Visual Studio does actually support this behavior if you are a bit careful when generating the proxy. If you check this box, you do get methods named Begin<method>/End<method>.

asyncclient

02 May, 2010

Get Silverlight installed: tips and tricks

I recently did a reinstall of my PC. As the final thing, Silverlight 4 was the one to be installed. But, this really proved troublesome as it failed when checking the prerequisites. The language was not correct? Come on – everything on my machine is English, so what’s that about?

image

So – what to do?

So – how does one get Silverlight Tools installed regardless the above? Well – I had success by using an alternative route:
1) Open the exe-file in WinRar (!)
2) Take out the msi-files present there
3) Install the msi-files in a ordinary fashion
4) you are good to go!

How do you know what msi-files to extract? Well – I took out the ones that were present in the exe-file seen here:
1) Silverlight_SDK.msi
2) RiaServices.msi
3) SilverlightTools_Package.msi
image

and installed them in the same order. Everything works fine afterwards and you are ready to start developing Silverlight applications (and version 4 that is!).

Really an unconventional route – but hey, it works!

iPhone/XCode - not all cases are equal!

This bit me! Having made some changes to an iPhone application (Obj-C); everything worked fine in the simulator. But, when deploying the s...