Design

Implementing a Plug-In Architecture in C#

By Shawn Walcheske, January 01, 2003

How to implement a plug-in architecture in C# using reflection and run-time inspection of type information.

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 applications tree view. The method goes through several steps:

Webcasts

By using the System.IO.Directory class, the code is able to do a wildcard search for all of the files with a matching .plug file extension. The Directory class static method GetFiles returns an array of System.String that 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.Assembly instance. The code that attempts to create an Assembly instance 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.IPlug interface.

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, a HostCommon.PlugNotValidException is 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.IPlug and defines all of the required attributes, it is wrapped in an instance of PlugTreeNode. The PlugTreeNode instance is then added to the host applications tree view.

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 IPlug interface. 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.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!