Nazgulled Posted November 30, 2006 Posted November 30, 2006 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: 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? Quote
MrPaul Posted November 30, 2006 Posted November 30, 2006 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: Quote Never trouble another for what you can do for yourself.
Nazgulled Posted November 30, 2006 Author Posted November 30, 2006 I've been trying to use System.Reflection and those methods you tell me about, but I can't seem to make them work... Quote
Leaders snarfblam Posted November 30, 2006 Leaders Posted November 30, 2006 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: 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. Quote [sIGPIC]e[/sIGPIC]
Nazgulled Posted November 30, 2006 Author Posted November 30, 2006 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... Quote
rbulph Posted November 30, 2006 Posted November 30, 2006 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? Quote
Nazgulled Posted November 30, 2006 Author Posted November 30, 2006 (edited) 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? Edited November 30, 2006 by Nazgulled Quote
rbulph Posted November 30, 2006 Posted November 30, 2006 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. Quote
Nazgulled Posted November 30, 2006 Author Posted November 30, 2006 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. Quote
Leaders snarfblam Posted December 1, 2006 Leaders Posted December 1, 2006 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: 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: 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: 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: 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. 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. Quote [sIGPIC]e[/sIGPIC]
Nazgulled Posted December 1, 2006 Author Posted December 1, 2006 (edited) 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... Edited December 1, 2006 by Nazgulled Quote
Nazgulled Posted December 1, 2006 Author Posted December 1, 2006 (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) Edited December 1, 2006 by Nazgulled Quote
MrPaul Posted December 1, 2006 Posted December 1, 2006 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. 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: Quote Never trouble another for what you can do for yourself.
MrPaul Posted December 1, 2006 Posted December 1, 2006 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. '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. '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. '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. '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: Quote Never trouble another for what you can do for yourself.
mskeel Posted December 1, 2006 Posted December 1, 2006 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. Quote
Nazgulled Posted December 1, 2006 Author Posted December 1, 2006 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... Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.