views:

164

answers:

2

I'm trying to put together a plugins system with .NET, and I'm not sure if I'm doing it correctly. The basis of the system is that a specific directory ({apppath}/Plugins/) will have a bunch of precompiled DLLs, and I want to look through each one with reflection, and for every class available, if it inherits a specific base class (this is defined in yet another DLL, but I'll go into that later), then create an instance of it and call a specific function in said instance.

Public Sub ScanPluginsInDirectory(ByVal Directory As String)

    Dim Plugins As New IO.DirectoryInfo(Directory)
    Dim Files As IO.FileInfo() = Plugins.GetFiles("*.dll")
    Dim CryptType As Type = GetType(CryptPluginBase)
    Dim PluginsData as List(Of LoadedPluginsInfo)

    For Each DllFile As IO.FileInfo In Files
        Try
            Dim thisAsm As Assembly = Assembly.LoadFrom(DllFile.FullName)
            Dim ClassDefs = thisAsm.GetTypes().Where(Function(type) CryptType.IsAssignableFrom(type))

            For Each ClassDef As Type In ClassDefs
                Dim A As Object
                A = ClassDef.Assembly.CreateInstance(ClassDef.Name)
                PluginsData.Add(New LoadedPluginsInfo(A.Plugin(), False))
            Next
        Catch ex As Exception
            Continue For
        End Try
    Next
End Sub

The specific problem I have is, I'm not sure this is the right way to do this. Would the method I'm trying to do work, if it can be assumed that A.Plugin() actually exists and any structures and classes referenced here are bug-free? If anyone would need more code in order to help, I can post it.

+2  A: 

Overall the strategy should work. The Assembly.LoadFrom call will load the target assembly into the process. From there it is possible to do type inspection and create instances of those types.

I think the easiest and most reliable way to create the instance is to use the Activator.CreateInstance method.

For Each def As Type in ClassDefs
  Dim inst = Activator.CreateInstance(def)
  PluginsData.Add(new LoadedPluginsInfo(inst.Plugin(), False))
Next

Depending on your goals, another suggestion would be to move the Try/Catch block into the loop instead of out of it. Having the Try/Catch block on the outside of the loop means that if any given type in an assembly does have an error, you will discard all types from that assembly. Moving it inside will allow you to discard only the types that do not work as expect.d The current behavior may be your intent though.

JaredPar
Do you mean inst.Plugin()?
Sukasa
@Sukasa, yes. I corrected it.
JaredPar
Okay, I wasn't sure if there was a little trick I wasn't aware of. Thanks!
Sukasa
+1  A: 

This should work, I've been using this kind of thing in some projects before. I specifically looked for a constructor and invoked it but apart from that it was the same idea.

But you might want to look at MEF, which takes care of a LOT of things for you for a plugin architecture (if you are willing to wait a bit for the release version, it's still CTP for now).

Denis Troller