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
Comments