Loading Plug-Ins
Listing 4 contains the method
LoadPlugs. LoadPlugs is located in HostForm.cs and is a
private instance method of the HostForm class. The LoadPlugs method
uses .NET reflection to load the available plug-in files, validates them as
plug-ins that can be used by the host application, and then adds them to the
host applications tree view. The method goes through several steps:
More Insights
White Papers
More >>Reports
- Strategy: 3 Steps to a Hands-Free Cloud
- Strategy: Smartphone Smackdown: Galaxy Note II vs. Lumia 920 vs. iPhone 5
Webcasts
More >>
- By using the
System.IO.Directoryclass, the code is able to do a wildcard search for all of the files with a matching.plugfile extension. TheDirectoryclass static methodGetFilesreturns an array ofSystem.Stringthat contains the absolute path of each file in the hard-coded path that matches the pattern. - After retrieving the array, the method next iterates through each file path attempting to load the file into a
System.Reflection.Assemblyinstance. The code that attempts to create anAssemblyinstance is wrapped in a try block. If the file is not a valid .NET assembly, then an exception is caught and feedback in the form of a message box is provided to the user about the unsuccessful attempt. If more file paths are left to process, the loop is able to continue. - With an assembly loaded, the code next iterates through each accessible type in the assembly to see if it supports the
HostCommon.IPluginterface. - If the type supports
HostCommon.IPlug, the code next validates that the type also supports the attributes that have been defined for plug-ins. If any of the attributes are not supported, aHostCommon.PlugNotValidExceptionis thrown. After the exception is thrown, it is caught and feedback in the form of a message box is provided to the user explaining why the plug-in failed. If more file paths are left to process, the loop is able to continue. - Finally, if the type supports
HostCommon.IPlugand defines all of the required attributes, it is wrapped in an instance ofPlugTreeNode. ThePlugTreeNodeinstance is then added to the host applications tree view.
Listing 4: The method LoadPlugs
private void LoadPlugs()
{
string[] files = Directory.GetFiles("Plugs", "*.plug");
foreach(string f in files)
{
try
{
Assembly a = Assembly.LoadFrom(f);
System.Type[] types = a.GetTypes();
foreach(System.Type type in types)
{
if(type.GetInterface("IPlug")!=null)
{
if(type.GetCustomAttributes(typeof(PlugDisplayNameAttribute),
false).Length!=1)
throw new PlugNotValidException(type,
"PlugDisplayNameAttribute is not supported");
if(type.GetCustomAttributes(typeof(PlugDescriptionAttribute),
false).Length!=1)
throw new PlugNotValidException(type,
"PlugDescriptionAttribute is not supported");
_tree.Nodes.Add(new PlugTreeNode(type));
}
}
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
}
return;
}
Deployment
The primary framework for the example application is deployed as two assemblies. The first assembly is Host.exe, and it houses the window forms host application. The second assembly is HostCommon.dll, and it houses all of the types that are used for communication between the host application and plug-ins. For example, the IPlug interface is deployed in HostCommon.dll so that it is equally accessible to both the host application and plug-ins. With the two assemblies in place, additional assemblies can be deployed that store the individual plug-ins. These assemblies are deployed to the plugs directory directly below the application path. The EmployeePlug class is deployed in the Employee.plug assembly, and the CustomerPlug class is deployed in the Customer.plug assembly. The example has taken the liberty of creating its own .plug file extension for plug-ins. The plug-in assemblies that are deployed are ordinary .NET library assemblies. Usually library assemblies are deployed with a .dll extension. The special extension has no influence on the runtime, but helps the plug-ins to stand out to users.
Alternative Designs
The design chosen for the example application is not exclusively correct. For example, using attributes is not required to develop a plug-in solution with C#. The services provided by the two defined attributes could also be provided by two property signatures added to the IPlug interface definition. Attributes were chosen because the name of a plug-in and its description are declarative in nature and fit quite nicely into the attribute model. Of course, using attributes requires more reflection code in the host application. This is a case-by-case design decision that developers will have to make on their own.
Conclusion
The example application is intended to be as thin as possible to help accentuate the communication between a host application and plug-ins. For a production environment, many improvements can be made to make a more useful solution. Some possible additions include:
- Increasing the communication points between the host and the plug-in by adding more method, property, and event signatures to the
IPluginterface. Additional interaction between the host and plug-ins will allow for plug-ins that can do more things. - Enabling users to explicitly select the plug-ins that are loaded.
Source Code
The complete source code for the sample application is available for download in walchesk.zip.
Notes
[1] Erich Gamma et al. Design Patterns (Addison-Wesley, 1995).
Shawn Patrick Walcheske is a software developer in Phoenix, Arizona. He is a Microsoft Certified Solution Developer and a Sun Certified Programmer for the Java 2 Platform.


