Introduction
Dynamic library loading is nothing new in native C++. In Microsoft Window’s implementation, this capability is realized with the use of Dynamic Link Library (DLL). This feature greatly expands the flexibility of an application where new features can be added / modified in the form of plug-in without the need to recompile the entire application. Similar capability is provided in .NET framework to dynamically load assemblies which make plug-in development for .NET based application possible. Moreover, dynamic assembly loading that .NET framework provides is much more powerful than native C++ DLL.
Wikipedia: plug-in is a software component that adds a specific feature to an existing software application.
Overview
Dynamic loading is only possible only when application and component are talking the same language. In such, both application and plug-in component need to communicate with each other using an agreed interface. The content of this interface cannot be changed for the entire lifetime, modifying the content of this interface required the application and all the components to be completely recompiled. See image below:
Implementation
An example of dynamic assembly loading projects is created as shown below for further discussion. This example is coded in C# using Visual Studio 2010 where both the application and plug-in assemblies are referenced to PluginInterface.dll.
DynamicLoadAssembly
: An application created to test dynamic load capability in .NET framework
PluginInterface
: Bridge that link between application and plug-in assemblies
Plugin_Sum
, Plugin_Multiplier
, Plugin_Calculation
: Plug-in assemblies which implement IPlugin
interface in PluginInterface.dll
The demo application contains 2 parts: Simple Test and Advance Test where Simple Test will load from a defined assembly whereas Advance Test will discover and load selected assemblies in the Plugins folder. Besides, a build configuration named “NewPlugin
” is created to simulate plug-in development where new plug-in is created without recompiling the main application.
IPlugin Interface
The IPlugin
interface is no different from any ordinary interface. It may contain property, methods (function) and events. Since the main application has no visibility of the classes implemented in plug-in assemblies, IPlugin
is the only access between application and plug-in assemblies.
public interface IPlugin
{
string Name {get; }
string GetDescription();
double GetLastResult { get; }
double Execute(double value1, double value2);
event EventHandler OnExecute;
void ExceptionTest(string input);
}
Load Assembly
As you can see, plug-in assemblies are not referenced by the main application except PluginInterface
. Assembly loading is done by using LoadAssembly
in System.Reflection
namespace. In this example, we assume that each plug-in assembly contains only one class that implements the IPlugin
interface. Theoretically, it is possible to have more than one class in an assembly which implement the IPlugin
interface, we leave it for you to explore further.
private PluginInterface.IPlugin LoadAssembly(string assemblyPath)
{
string assembly = Path.GetFullPath(assemblyPath);
Assembly ptrAssembly = Assembly.LoadFile(assembly);
foreach (Type item in ptrAssembly.GetTypes())
{
if (!item.IsClass) continue;
if (item.GetInterfaces().Contains(typeof(PluginInterface.IPlugin)))
{
return (PluginInterface.IPlugin)Activator.CreateInstance(item);
}
}
throw new Exception("Invalid DLL, Interface not found!");
}
Once the assembly is successfully loaded into the application, it can be used just like a normal class instance regardless of whether it is a function call or an event subscription. An example of event subscription on loaded assemblies is shown as below:
currPlugin = LoadAssembly(".\\Plugins\\" + cbAssemblies.Text + ".dll");
currPlugin.OnExecute += new EventHandler(currPlugin_OnExecute); //Subscribe Event.
Limitation
Despite its high flexibility, there is one limitation in dynamic assemblies loading where there is no way to unload the loaded assemblies until the main application is terminated, even with the System.AppDomain
will not release the loaded assembly after AppDomain.Unload
is called.
Exception Handling
Exceptions can traverse from loaded assemblies to main application without much problem. The same try
... catch
block is sufficient to handle exception raised from assembly unlike native C++ DLL where errors are normally returned as error codes and error messages.
History
- 10-Oct-2013: Initial release (Version 1.0.0)