Introduction
Back in the old days when I programmed with Visual C++ and DCOM, one of my favorite features was ‘Component Categories’. For those of you who are unfamiliar with DCOM, categories allowed the component developer to dynamically add behavior to an application by supporting a common interface and GUID pair. On application start-up, all registered components who supported a particular GUID could be identified and instantiated and subsequently communicated with via the common interface.
So far I have been unable to identify an equivalent in .NET Remoting, however after reading an article on .NET Reflection here on Code Project (the original source for which I can’t find but if you think you’re the author then contact me and I’ll give you your due credit), I think I’ve come up with something just as flexible.
Reflection
Reflection is nothing new to the Java development community, the basic idea is that metadata for all managed types can be accessed at runtime. This gives the developer the ability to interrogate the inheritance hierarchy of an object, invoke methods similar to IDispatch
invoke, but most importantly to instantiate an object from its metadata definition. Couple this with the .NET ability to interrogate the exposed managed object types contained within a particular assembly, and you have the fundamental building blocks for a ‘Component Category’ alternative.
At this point, I’d like to point out that this Plugin API should not be seen as only applicable to Remote Object hosting. In fact, at the moment I use the API to add a vertical slice of functionality to my application space, from Menu options and Wizards to backend servers.
Plugin API
The basic Plugin API is quite simple; a PluginManager
class is provided which takes a ‘Plugin Interface Type’ and can then return all the objects contained within the current working directory supporting that interface. The reason for the indirection between the Plugin interface and the Plugin object is that I wanted to be able to control the instantiation of the Plugin object, possibly through the use of a sub-class of the basic PluginManager
class.
public interface IPlugin
{
Type PluginInterface {get;}
}
public class PluginManager
{
...
public virtual ArrayList GetPlugins(Type pluginType)
{
if(pluginType ==null)
{
Console.WriteLine("The plugin type was not set");
return new ArrayList(0);
}
try
{
string currentDirectory =
System.AppDomain.CurrentDomain.BaseDirectory;
string[] pluginFilenames = Directory.GetFiles(
currentDirectory, @"*.dll");
ArrayList plugins = new ArrayList();
foreach(string pluginFilename in pluginFilenames)
{
Assembly assembly = Assembly.LoadFrom(pluginFilename);
Type[] assemblyPlugins = assembly.GetExportedTypes();
foreach(Type plugin in assemblyPlugins)
{
if(plugin.GetInterface(pluginType.FullName)==null)
{
continue;
}
plugins.Add(Activator.CreateInstance(plugin));
}
}
return plugins;
}
catch(BadImageFormatException bife)
{
Console.WriteLine(bife.ToString());
return new ArrayList(0);
}
}
}
Remote Plugin API
The Remote Plugin API is basically a standardization on the definition of a Plugin interface and the creation of a custom Remote Plugin Manager.
The Remote Plugin Interface
public interface IRemotePlugin : IPlugin
{
string ObjectUri {get;}
WellKnownObjectMode ActivationType {get;}
}
The Remote Plugin Manager then delegates to the base Plugin Manager to identify the available plugins and subsequently register the objects for hosting within the current App Domain.
public class RemotePluginManager : PluginManager
{
...
public void RegisterPlugins()
{
ArrayList plugins = this.GetPlugins(typeof(IRemotePlugin));
foreach(IRemotePlugin plugin in plugins)
{
RemotingConfiguration.RegisterWellKnownServiceType(
plugin.PluginInstance,
plugin.ObjectUri,
plugin.ActivationType);
}
}
}
Generic Hosting Application
The creation of a generic hosting application to use this Remote Plugin Manager is then relatively straight forward. On startup, standard Remote objects can be configured using the normal XML configuration file followed by pluggable object configuration via the Remote Plugin Manager.
class RemoteHostApp
{
[STAThread]
static void Main(string[] args)
{
try
{
Console.Write("Remote Host Application Starting ");
Console.Write(DateTime.Now.ToLongDateString() + ":");
Console.WriteLine(DateTime.Now.ToLongTimeString());
RemotingConfiguration.Configure("RemoteHostApp.exe.config");
RemotePluginManager pluginManager =
new RemotePluginManager();
pluginManager.RegisterPlugins();
ShowWellKnownServices();
System.Console.Write("Hit any key to exit: ");
System.Console.ReadLine();
Console.Write("CMS Server stopped ");
Console.Write(DateTime.Now.ToLongDateString() + ":");
Console.WriteLine(DateTime.Now.ToLongTimeString());
}
catch(Exception e)
{
Trace.WriteLine(e);
}
}
...
}
Deployment
All that needs to be done to deploy a new Remote object is to copy the DLL assembly into the working directory of the RemoteHostApp application. The hosting app presented here is very simple in that, to pick up the new Remote object it would have to be restarted. A very simple improvement to overcome this necessity would be to create a directory listener to detect when new files are added and existing one deleted.
Summary
In itself the Pluggable Remote API isn’t much of an improvement over the XML Deployment file, however when the pluggable concept is taken straight through to the GUI level, it becomes possible to add complete vertical slices of functionality to a core platform.
Downloads
Note: Source and demo projects have been created using #Develop.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.