Writing Plugin-Based Applications

The Host Application

We create a new project, of type Windows Application. The first thing to do is reference the class library we just created, and set the build output to the same directory.

The main content of this article is the process of examining DLLs to see if they contain plugins, storing the information about what plugins are available, and instantiating and using them. To do this, I will provide a class, PluginServices.vb, which will encapsulate all these things.

To get our list of plugins, we use the function FindPlugins which accepts a string containing the directory to search in, and a string with the full name of the interface we're looking for classes that implement. This function enumerates over all files with the extension .dll in the directory supplied, loads them using Assembly.LoadFrom() and passes execution to another function to examine the assembly.

    Public Shared Function FindPlugins(ByVal strPath As String, ByVal strInterface As _
    String) As AvailablePlugin()
        Dim Plugins As ArrayList = New ArrayList()
        Dim strDLLs() As String, intIndex As Integer
        Dim objDLL As [Assembly]
        'Go through all DLLs in the directory, attempting to load them
        strDLLs = Directory.GetFileSystemEntries(strPath, "*.dll")
        For intIndex = 0 To strDLLs.Length - 1
            Try
                objDLL = [Assembly].LoadFrom(strDLLs(intIndex))
                ExamineAssembly(objDLL, strInterface, Plugins)
            Catch e As Exception
                'Error loading DLL, we don't need to do anything special
            End Try
        Next
        'Return all plugins found
        Dim Results(Plugins.Count - 1) As AvailablePlugin
        If Plugins.Count <> 0 Then
            Plugins.CopyTo(Results)
            Return Results
        Else
            Return Nothing
        End If
    End Function

Once all files have been examined, the function returns an array of type AvailablePlugin if some were found, or Nothing if none were found. As you can see, this function calls ExamineAssembly to inspect a loaded assembly.

The ExamineAssembly function enumerates all types exported by the loaded assembly, and uses the GetInterface() method of each type to see if it implements our interface. Conveniently, this method takes a string containing the fully qualified name of the interface. In this case, it's "PluginSample.Interfaces.IPlugin" we're looking for. If a type is found that implements the interface, an entry is added to the ArrayList with the full path of the DLL and the full name of the class.

    Private Shared Sub ExamineAssembly(ByVal objDLL As [Assembly], _
    ByVal strInterface As String, ByVal Plugins As ArrayList)
        Dim objType As Type
        Dim objInterface As Type
        Dim Plugin As AvailablePlugin
        'Loop through each type in the DLL
        For Each objType In objDLL.GetTypes
            'Only look at public types
            If objType.IsPublic = True Then
                'Ignore abstract classes
                If Not ((objType.Attributes And TypeAttributes.Abstract) = _
                TypeAttributes.Abstract) Then
                    'See if this type implements our interface
                    objInterface = objType.GetInterface(strInterface, True)
                    If Not (objInterface Is Nothing) Then
                        'It does
                        Plugin = New AvailablePlugin()
                        Plugin.AssemblyPath = objDLL.Location
                        Plugin.ClassName = objType.FullName
                        Plugins.Add(Plugin)
                    End If
                End If
            End If
        Next
    End Sub

Lastly, we write the function that will be used to create an instance of a plugin where needed. It accepts an AvailablePlugin structure and returns an Object, to be casted to the appropriate type by the calling procedure.

    Public Shared Function CreateInstance(ByVal Plugin As AvailablePlugin) As Object
        Dim objDLL As [Assembly]
        Dim objPlugin As Object
        Try
            'Load dll
            objDLL = [Assembly].LoadFrom(Plugin.AssemblyPath)
            'Create and return class instance
            objPlugin = objDLL.CreateInstance(Plugin.ClassName)
        Catch e As Exception
            Return Nothing
        End Try
        Return objPlugin
    End Function

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“If debugging is the process of removing software bugs, then programming must be the process of putting them in.” - Edsger Dijkstra