Plugin-Based Application - How to access random functions?

Nazgulled

Centurion
Joined
Jun 1, 2004
Messages
119
I'm using the tutorial from divil (http://www.divil.co.uk/net/articles/plugins/plugins.asp) to develop plugin-based applications but I'm having some problems that I don't have a clue on how to solve them...

For instance, in the interfaces plugin:
Code:
Public Interface IPlugin

    Sub Initialize(ByVal Host As IHost)

    ReadOnly Property Name() As String

    Function Calculate(ByVal int1 As Integer, ByVal int2 As Integer) As Double

End Interface

We are pre dictating that we only have a Sub called Initialize, a ReadOnly Property called Name and a Function called Calculate. However, I want the plugins written to my application to have their own functions (the many they want, public, private, whatever...) with their own names, than I want to be able to use them on my main application.

For one part of this, I though I could create a string array, listing the names of the public functions that the plugin author want to be used by the main application. But than the problem came... How can I have the plugin author create their own functions with their own names and allow me to use them in the main function, how can I do that?
 
System.Reflection

Using Reflection, you can inspect a type to find its members, and also invoke those members. The System.Reflection namespace contains the classes that implement this.

For example, the Type class has methods GetMethods and GetProperties which can be used to discover what members the type contains, and a method InvokeMember which can invoke members.

You might be interested in these threads:

Reflector Class - Reflection Made Easy
Is it possible for a Base Class to call a function in a Child Class?
Access class properties in code (property names in datatable)

Good luck :cool:
 
I've been trying to use System.Reflection and those methods you tell me about, but I can't seem to make them work...
 
I can convert my reflector class that MrPaul linked to to VB, if you would like. Using reflection can actually be a bit tricky. I recommend that you investigate other possible solutions, however, even if you ultimately resort to reflection. For one thing, reflection is much slower than using an interface. For another, certain things such as method overloading and differing accessor accessibility can throw off reflection code that might normally work. One suggestion would be that your plug-in interface implement some sort of command interface. For example, something along the lines of:
Visual Basic:
Public Interface ICommandInterface
    Public Function GetCommands() As IEnumerable 'Or more specific or array type
    Public Function InvokeCommand(command As String, args As Object())
End Interface
 
Public Interface IPlugIn
    Inherits ICommandInterface
  
    'Declare your standard plugin interface members here
End Interface
The plug-in class would have to declare a method (GetCommands) that returns, say, a list of CommandInfo objects, each of which defines a command name and expected arguments, and it would have to declare a function (InvokeCommand) which executes a command. (This would be similar to the way commands are sent to the mciSendStringA() method of winmm.dll.) It might not be the best solution, and it would be difficult for beginners to implement, but it is really just an example of how it could be done without reflection.
 
There's no need to convert it from C# to VB, I can easily read it either way. Thanks anyway. I will investigate other possibilities...
 
Sounds very odd to me. How will your application know what to do with the functions contained in the plug-ins once it knows what they are? If, as you seem to be suggesting in your title, you want to access random functions, can't you incorporate the randomness in your plug-ins rather than the application?
 
Maybe I chose wrong title, I didn't mean exactly random, what I meant is that one plugin may have 2 functions, another one may have 3 with completely different method names...

@marble_eater
Your method described earlier seems to be the best option, however, I'm having problems working with it, can you please provide a more complete example?
 
Last edited:
Nazgulled said:
Maybe I chose wrong title, I didn't mean exactly random, what I meant is that one plugin may have 2 functions, another one may have 3 with completely different method names...
In which case I'm baffled as to what you want this for. If you want all functions to be called in sequence, just do this in your plug-in.
 
It doesn't matter what I want this for, I want to do it and I know why I want it this way... And no, I don't want to call them in sequence I just want to call functions in the plugins. Functions that I don't know their names nor I want to specify them in the host interface cause I don't want to limit the plugins to a number of functions and/or names.
 
If you explain how and why these functions will be called it is much easier to give a satisfactory answer. As for not being able to get reflection to work, I don't know where you might have run into problems and you did not explain. Reflection might be the answer you are looking for, I merely suggested you look into other options as well.


As for the example you requested, well, since I am such a nice guy, here is a bunch of code. Like I said, this isn't a good beginner project. I'm assuming that you are familiar with inheritance and interfaces (especially since you are writing a plug-in app), and that you know how to use the System.Activator class and the PropertyGrid control.

Since we will be creating command-based plug-in, we would want to write a command-based-plug-in-interface as a base for command-based plug-ins.

The commands will be invoked by a string (which specifies the name of the command) and if there are any arguments, they can be passed using a special class for arguments for that command. You will see what I mean by that in a moment. Here is our interface:
Visual Basic:
Interface ICommandInterface
    Function GetCommands() As CommandInfo() 'Gets an array of CommandInfo objects
    Function SendCommand(command As String, args As Object) As Object
End Interface
We need to declare a class that can describe a command:
Visual Basic:
Public Structure CommandInfo
    Public Name As String
    Public ArgType As System.Type
   
    Public Sub New(Name As String, ArgType As System.Type)
        Me.Name = Name
        Me.ArgType = ArgType
    End Sub
End Structure
Note the ArgType. This specifies the class that is used to pass arguments to a command. This is very similar to the way arguments are passed to event handlers: A click event receives an EventArgs object, and a MouseDown event receives a MouseEventArgs. A label edit in a list view receives a LabelEditEventArgs. Get it? You will see how it works soon.

Now, pretend we are making an graphics editing application which supports graphic filter plug-ins. We can actually use our ICommandInterface as is because it is flexible enough that we don't need to add any methods--everything can be done through the SendCommand method.

This is how a graphics filter plugin will work: the GetCommands function will return a list of commands that represent filters that the plug-in supports, and you call SendCommand to use a filter. Simple, no?

Here is our plug-in code:
Visual Basic:
Public Class BitmapFilterPlugIn
    Implements ICommandInterface
 
    Public Function GetCommands() As CommandInfo() Implements ICommandInterface.GetCommands
        'We don't have any filters yet, so return an empty array
        Return New CommandInfo(0) {}
    End Function
 
    Public Function SendCommand(ByVal command As String, ByVal args As Object) As Object Implements ICommandInterface.SendCommand
        'We don't have any filters yet, so do nothing and return nothing.
        Return Nothing
    End Function
End Class
[/vB]
It doesn't look like much yet, but we will add filters. First, we need to look at those argument objects I was talking about. Since all filters will work on Bitmap objects, we will need to pass bitmaps to our filters. Let's define a base  class for passing arguments to our filters.
[CODE=visualbasic]
Public Class FilterOptions
    Public TargetImage As Bitmap
End Class
Now, let's create a filter. We can do something simple, like Brightness. Generally you would pass a floating-point number to a brightness filter that represents the percentage of brightness the filter is to apply. Here is a class that we can pass to the Brightness filter:
Visual Basic:
Public Class BrightnessOptions
    Inherits FilterOptions
 
    'Create a property for a filter option
    Dim _Brightness As Single
    Public Property Brightness() As Single
        Get
            Return _Brightness
        End Get
        Set(ByVal value As Single)
            _Brightness = value
        End Set
    End Property
End Class
Now, let's incorporate that filter into our filter plug-in.
Visual Basic:
Public Class BitmapFilterPlugIn
    Implements ICommandInterface
 
    Public Function GetCommands() As CommandInfo() Implements ICommandInterface.GetCommands
        'Return an array that lists our commands and the arguments they require
        Return New CommandInfo() { _
            New CommandInfo("Brightness", GetType(BrightnessOptions)) _
        }
    End Function
 
    Public Function SendCommand(ByVal command As String, ByVal args As Object) As Object Implements ICommandInterface.SendCommand
        'Perform the appropriate action based on the command string
        Select Case command.ToUpper()
            Case "BRIGHTNESS"
                Brighten(CType(args, BrightnessOptions).TargetImage, _
                        CType(args, BrightnessOptions).Brightness)
        End Select
 
        ' Most likely, none of our filters will need to return anything
        Return Nothing
    End Function
 
    Public Sub Brighten(ByVal image As Bitmap, ByVal brightness As Single)
        'This function applies the filter
    End Sub
End Class
Now everything should come together. To use the plug-in, first you would call GetCommands(), which would return an array with one CommandInfo. That CommandInfo would tell you that there is a command named Brightness which requires a BrightnessOptions object.

We will list Brightness in our plug-in menu. When the user selects the Brightness filter, we will use the System.Activator class to create a BrightnessOptions object. We will assign the image the user is editing to the BrightnessOptions object's TargetImage field, and then we will put the BrightnessOptions object in a property grid where the user can edit the Brightness property (or whatever options there are for other filters). You will most likely want to show the property grid on a dialog form.

Finally, we call the SendCommand function, passing the string "Brightness" and our BrightnessOptions object. The plug-in calls the Brighten() function, which actually applies the filter, and that's it.
 
But those things you are asking don't really matter, that's up to me and my application, I don't think it will really help me explaining how exactly my whole application will work and how the plugins system will work... And besides, I don't know exactly how the plugins system will work caus I've just thought on how to do it and I'm seeing if it's a viable solution, but I can't really test it cause I have the problem described on this thread.

Anyway, the application I'm coding has to do with network connection cards, to configure ips and such with the ability to have profiles and just apply one of them. I want to add the plugins system so users can extend the functionality of the profile selection to other stuff. For instance, the IE proxy, that's the first plugin I'm developing to release with the final version of the application. That plugin would have 2 functions, one to set proxy in the registry and the other to clear it. However, other plugins may do other stuff or may change proxies too on some other programas but may need more functions to be called or maybe just one. What I want to do, is to allow the plugins to have functions they want, then, "share" the ones that can be used by the main application. When creating a new profile, the user selects the plugins he wants to use, then, for each plugin, selects the functions that he wants to be called when that profile is applied. For instance, you would create a profile for "home" with the IE plugin, calling just the function to clear the proxy and another profile for "university" with the IE plugin, calling just the function to set the proxy in the registry (that's actually an example for the real use I'm going to give to my program).

I'm no begginer, but I'm not that average user either, I just now some stuff, maybe just a bit more than a begginer, but not too much... Maybe cause english is not my main language and there are many words in the documentation to describe stuff that I don't really understand... Shure I look up for words in the dictionary and google, shure I get the meaning of them, but sometimes that doesn't really help to understand the concept and the real meaning of them in context of conding in VB.NET. A bunch of things you just said, I don't understand that well... I didn't knew Interfaces exist nor what were they, until I read the tutorial by devil and still, even after reading msdn, I don't really get what exactly are they, what are they for and how exactly they work. I just know a bit and understand this and that, but the whole concept, no... I always had difficulties in this field, even in my home language, but I don't really read documentation in my language so, I don't think it's a valid point.

I'm going to read this whole code you posted, see if I can understand it and implement it on my project... I'll report back later! Thanks.

I'm sorry giving you this whole work...
 
Last edited:
I've been trying to use your code in my test application but I've failed... I can't seem to understand enough to take this and apply it to my application.

Did you take a look at the personal message I've sent you? Did you opened the solutions inside the zip file? Do I really need all of the above code to do what I want? (just create a function2() in the plugin without implementing it in the interfaces)
 
Last edited:
Consider delegates

You have to decide whether you want to place the responsibility for this functionality on the shoulders of the host application or on the plugins. In the case of the former, you need to look into reflection. marble_eater's code does the latter.

Nazgulled said:
Do I really need all of the above code to do what I want? (just create a function2() in the plugin without implementing it in the interfaces)

The code marble_eater posted is fairly concise, and would form the basis of the system. If you wanted to extend BitmapFilterPlugIn with another method, you'd only need to add two more lines of code within SendCommand and alter the function list in GetCommands. That is not a lot of code. I can't see how you are able to judge the complexity of the code given without some basic concept of what it is you are trying to achieve and what basic steps are required - for example, knowing what the purpose of an interface is.

Anyway, as far as you have described the purpose of this feature, I would recommend against both reflection and this SendCommand approach. Instead, I suggest the GetCommands function should return a collection of delegates along with their names, and then the host application can invoke them as required. On a more advanced level, the application can also inspect the delegates to determine the number and types of parameters.

Good luck :cool:
 
Delegates example

I hope this doesn't confuse you further. This is an example of using delegates to perform this.

First, define the signature of any methods you want to expose, and again define the plugin interface. In this example, the interface only contains the GetMethods method. Other members can be added in line with your application requirements. You could also define different delegates for different parameter combinations and return types, with suitable changes to GetMethod.

Visual Basic:
'Defines members a plugin MUST implement
Public Interface IPluginInterface
    Function GetMethods() As PluginDelegate()
End Interface

'Defines the signature of any custom plugin methods
Public Delegate Sub PluginDelegate()

Now, for a simple plugin, the IEPlugin described in your previous posts. The implementation of GetMethods returns a list of available methods - in this case just SetProxy. Note that the signature of SetProxy must match PluginDelegate.

Visual Basic:
'Has just one callable method - SetProxy
Public Class IEPlugin
    Implements IPluginInterface

    'Returns a list of callable methods
    Public Function GetMethods() As PluginDelegate() Implements IPluginInterface.GetMethods
        Return New PluginDelegate(0) {AddressOf SetProxy}
    End Function

    Public Sub SetProxy()
        'Set proxy in registry or whatever
    End Sub
End Class

Now, how a more complex class might look, containing three methods instead of one.

Visual Basic:
'Has three callable methods - MethodOne, MethodTwo, MethodThree
Public Class MoreComplexPlugin
    Implements IPluginInterface

    'Returns a list of callable methods
    Public Function GetMethods() As PluginDelegate() Implements IPluginInterface.GetMethods
        Return New PluginDelegate(2) {AddressOf MethodOne, AddressOf MethodTwo, AddressOf MethodThree}
    End Function

    Public Sub MethodOne()
        'Do a little dance
    End Sub
    Public Sub MethodTwo()
        'Make a little love
    End Sub
    Public Sub MethodThree()
        'Get down tonight
    End Sub
End Class

Finally, the code in the host application which calls the GetMethods method on a plugin then inspects the returned delegates to find their names. The delegates are then stored in a dictionary to be accessed by name later - in the CallPluginMethod method.

Visual Basic:
'Host app
Public Class HostApplication

    'Holds all methods we can use: key = Typename.Methodname
    Private m_methods As Dictionary(Of String, PluginDelegate)

    'Constructor
    Public Sub New()
        m_methods = New Dictionary(Of String, PluginDelegate)
    End Sub

    'Gets all methods from plugin and adds to dictionary
    Public Sub GetPluginMethods(ByVal plugin As IPluginInterface)

        Dim methods As PluginDelegate()
        Dim ptype As Type

        'Get type of plugin
        ptype = DirectCast(plugin, Object).GetType()

        'Get methods
        methods = plugin.GetMethods()
        For Each pd As PluginDelegate In methods
            'Get name of method and add to m_methods
            m_methods.Add(ptype.Name & "." & pd.Method.Name, pd)
        Next
    End Sub

    'Invokes a plugin method
    Public Sub CallPluginMethod(ByVal pluginname As String, ByVal methodname As String)

        Dim fullname As String
        Dim plugmethod As PluginDelegate

        'Find the method
        fullname = pluginname & "." & methodname
        plugmethod = m_methods(fullname)

        If (plugmethod IsNot Nothing) Then
            'Found method, call it!
            plugmethod()
        Else
            'Method was not found
        End If

    End Sub


End Class

Good luck :cool:
 
Nazgulled said:
just create a function2() in the plugin without implementing it in the interfaces

Why not use attributes to define "callable" methods? NUnit is a good example of what I am talking about. You would still use reflection to grab the details, but you wouldn't be bound by an interface definition.
 
Hum... You didn't confuse me... Although I don't know exactly what are delegates, I took your example and implemented it correctly (I guess) in my test application. Everything seems to be working the way I want it...

Sorry giving you guys all this work in help me out...
 
Back
Top