mooman_fl Posted March 6, 2009 Posted March 6, 2009 This is related to the plugin project I mentioned on the other thread. The app is set up as a User Control that find and loads plugins. The main interface it checks for to determine a valid plugin is within the control assembly. The plugin references this interface (IPlug.IPluginInformation) as does the class that I am trying to DirectCast to. I do one thing a little out of the ordinary however: I load a new AppDomain and this part of the code is within the second AppDoman. It iterates through the Types in the plugin assembly just fine, and correctly determins that it implements the desired interface. I can use Assembly.CreateInstance to get a reference to the Class Object, however when I try to DirectCast it to the PluginInformation class that also Implements the same interface, I get: Unable to cast object of type 'Plugin1.PluginInfo' to type 'IPlug.IPlugInfo'. Plugin1.PluginInfo is the name of the class in the plugin that implements the IPlug.IPlugInfo interface. I am a little stumped as to what is the problem. Is it because I am doing this in second AppDomain? If so, why is it even recognizing the interface to begin with? Here is the code for the method that does this: Friend Function GetPluginInfo(ByVal FullPath As String) As PluginInformation Dim tempAssembly As [Assembly] Dim IPlugName As String = GetType(IPlug.IPlugInfo).ToString Dim temp As Type Dim tempRef As Object Dim tempPlugInfo As PluginInformation tempAssembly = [Assembly].LoadFile(FullPath) For Each Exported As Type In tempAssembly.GetTypes 'cycle through the types that are in the assembly If Exported.IsPublic Then If Not ((Exported.Attributes And TypeAttributes.Abstract) = TypeAttributes.Abstract) Then temp = Exported.GetInterface(IPlugName, True) 'checks for a match against default interface If Not (temp Is Nothing) Then 'if it is there, set the flag tempRef = tempAssembly.CreateInstance(Exported.FullName) 'when it hits the following line it gives the error tempPlugInfo = DirectCast(tempRef, IPlug.IPlugInfo) End If End If End If Next Return tempPlugInfo End Function Any help would be appreciated. Quote "Programmers are tools for converting caffeine into code." Madcow Inventions -- Software for the Sanity Challenged.
Administrators PlausiblyDamp Posted March 6, 2009 Administrators Posted March 6, 2009 Just because PluginInformation and tempRef happen to have an interface in common doesn't mean you can cast one to the other, you could cast either to the IPlug.IPlugInfo interface as they do have this in common. An integer and a string both implement the IConvertable interface, this doesn't mean you can cast a string to an integer though. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
mooman_fl Posted March 6, 2009 Author Posted March 6, 2009 (edited) Just because PluginInformation and tempRef happen to have an interface in common doesn't mean you can cast one to the other, you could cast either to the IPlug.IPlugInfo interface as they do have this in common. I understand that. However, after making a new variable called tempInterface as IPlug.IPlugInfo I tried casting tempRef to that instead. I got the exact same error. The type I am trying to cast ONLY implements that interface with nothing added. This is the IPlug.IPlugInfo interface: Public Interface IPlugInfo ReadOnly Property Name() As String ReadOnly Property Version() As String ReadOnly Property Description() As String ReadOnly Property Company() As String ReadOnly Property Creator() As String ReadOnly Property Copyright() As String ReadOnly Property Email() As String ReadOnly Property Homepage() As String End Interface And this is the Plugin1.PluginInfo that I am trying to cast: Public Class PluginInfo Implements IPlug.IPlugInfo Public ReadOnly Property Company() As String Implements IPlug.IPlugInfo.Company Get Return "Madcow Inventions" End Get End Property Public ReadOnly Property Copyright() As String Implements IPlug.IPlugInfo.Copyright Get Return "©2007" End Get End Property Public ReadOnly Property Creator() As String Implements IPlug.IPlugInfo.Creator Get Return "Tim Browning" End Get End Property Public ReadOnly Property Description() As String Implements IPlug.IPlugInfo.Description Get Return "First dummy plugin for testing" End Get End Property Public ReadOnly Property Email() As String Implements IPlug.IPlugInfo.Email Get Return "mooman_fl@yahoo.com" End Get End Property Public ReadOnly Property Homepage() As String Implements IPlug.IPlugInfo.Homepage Get Return "http://www.madcowinventions.com/" End Get End Property Public ReadOnly Property Name() As String Implements IPlug.IPlugInfo.Name Get Return "Single Digit Plugin" End Get End Property Public ReadOnly Property Version() As String Implements IPlug.IPlugInfo.Version Get Return "1.0" End Get End Property End Class Maybe there is a reason why one can't cast to the other, but I am just not seeing it. As I mentioned, the main control calls this from within another AppDomain... could that cause the problem? Edited March 6, 2009 by mooman_fl Quote "Programmers are tools for converting caffeine into code." Madcow Inventions -- Software for the Sanity Challenged.
Administrators PlausiblyDamp Posted March 6, 2009 Administrators Posted March 6, 2009 A type that implements an interface is different from the interface itself, you cannot cast between two different types. e.g. Public Interface ITest Function TestFunc() As Integer End Interface Public Class TestClassOne Implements ITest Public Function TestFunc() As Integer Implements ITest.TestFunc Return 0 End Function End Class Public Class TestClassTwo Implements ITest Public Function TestFunc() As Integer Implements ITest.TestFunc Return 0 End Function End Class Even though both classes are nothing more than the same implementation of the ITest interface they are not the same type and any attempt to cast e.g. Dim t1 As New TestClassOne Dim t2 As TestClassTwo t2 = DirectCast(t1, TestClassTwo) will not work. If you have Option Strict On set (and you should IMNSHO) this code will not even compile. If you were to jest return the interface rather than a type based on the interface everything should work just fine. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
mooman_fl Posted March 7, 2009 Author Posted March 7, 2009 (edited) Ok, I am getting the feeling that I am just stuck on stupid because I am not getting what you are saying. What am I doing different than is done in previous plugin examples (the one done by Divil and the one by myself based on his)? In both examples you make an interface that both your app and the plugin implement. At runtime you find the class in the plugin that implements that interface, then load it and cast it to the common interface which then allows you access to the properties, methods, etc. As far as I can tell, I am doing pretty much the exact same thing as in the plugin examples I mentioned, yet I am getting this error on the casting. Here, I am just going to attach my project minus executables. The above code is in Gateway.vb in the Shim folder within the PowerPlug project. It is called near the end of the StartPluginEngine method in PowerPlug.vb. I normally don't ask others to fix something for me, so if you could just take a look and give me a better idea of how what I am doing is wrong. What the program currently does correctly until it hits this code is accept a directory where plugins can be found, and accept a list of assemblies that carry additional interfaces that constitute a valid plugin. IPlug.IPlugInfo is included in the control assembly and all plugins reference that assembly to the get the interface. At run time makes a collection listing all files in the plugin directory, and validates them against the needed plugin interfaces. For each valid plugin assembly it sets a property (IsPlugin) to "True". So far all of this works correctly. The next step I was working on was to pull the information from the valid plugins that reside in the plugin directory into the class implementing the IPlug.IPlugInfo and populate the Plugins.PluginInformation part of the collection with the data in it. This is where it is getting the error. As a side note, since this is all done before the user chooses to load a plugin, this is done inside another AppDomain so that after this initial information is gathered the references to the Assemblies, and their associated resources, are freed. It is a VS2005 project.PowerPlug.zip Edited March 7, 2009 by mooman_fl Quote "Programmers are tools for converting caffeine into code." Madcow Inventions -- Software for the Sanity Challenged.
Administrators PlausiblyDamp Posted March 7, 2009 Administrators Posted March 7, 2009 After a couple of changes it now seems to run without any errors. In the gateway.vb file I changed the following 'Delegate declaration to Public Delegate Function _GetPluginInformation(ByVal FullPath As String) As IPlug.IPlugInfo and the GetPluginInfo method to Friend Function GetPluginInfo(ByVal FullPath As String) As IPlug.IPlugInfo Dim tempAssembly As [Assembly] Dim IPlugName As String = GetType(IPlug.IPlugInfo).ToString Dim temp As Type Dim tempRef As Object Dim tempPlugInfo As IPlug.IPlugInfo tempAssembly = [Assembly].LoadFile(FullPath) For Each Exported As Type In tempAssembly.GetTypes 'cycle through the types that are in the assembly If Exported.IsPublic Then If Not ((Exported.Attributes And TypeAttributes.Abstract) = TypeAttributes.Abstract) Then temp = Exported.GetInterface(IPlugName, True) 'checks for a match against default interface If Not (temp Is Nothing) Then 'if it is there, set the flag tempRef = tempAssembly.CreateInstance(Exported.FullName) 'when it hits the following line it gives the error tempPlugInfo = DirectCast(tempRef, IPlug.IPlugInfo) End If End If End If Next Return tempPlugInfo End Function Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
mooman_fl Posted March 7, 2009 Author Posted March 7, 2009 (edited) Thats odd. I made those same changes yesterday after your original explaination, and still got the error. So I thought I was just misunderstanding you. However after cutting and pasting your solution, it is still giving that error: Unable to cast object of type 'Plugin1.PluginInfo' to type 'IPlug.IPlugInfo'. Can you think of any reason why it might be giving an error on one computer, but not the other? My computer is an HP Pavilion Athlon 64 X2 running Vista 32bit Home with 2GB RAM. Here is the complete exception detail: System.InvalidCastException was unhandled Message="Unable to cast object of type 'Plugin1.PluginInfo' to type 'IPlug.IPlugInfo'." Source="PowerPlug" StackTrace: at PowerPlug.Gateway.GetPluginInfo(String FullPath) in C:\Users\Aarons\Documents\Visual Studio 2005\Projects\PowerPlug\PowerPlug\Shim\Gateway.vb:line 81 at PowerPlug.Gateway.GetPluginInfo(String FullPath) at PowerPlug.PowerPlug.StartPluginEngine() in C:\Users\Aarons\Documents\Visual Studio 2005\Projects\PowerPlug\PowerPlug\PowerPlug.vb:line 200 at TestApp.Form1.Form1_Load(Object sender, EventArgs e) in C:\Users\Aarons\Documents\Visual Studio 2005\Projects\PowerPlug\TestApp\Form1.vb:line 22 at System.EventHandler.Invoke(Object sender, EventArgs e) at System.Windows.Forms.Form.OnLoad(EventArgs e) at System.Windows.Forms.Form.OnCreateControl() at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible) at System.Windows.Forms.Control.CreateControl() at System.Windows.Forms.Control.WmShowWindow(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ScrollableControl.WndProc(Message& m) at System.Windows.Forms.ContainerControl.WndProc(Message& m) at System.Windows.Forms.Form.WmShowWindow(Message& m) at System.Windows.Forms.Form.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) at System.Windows.Forms.SafeNativeMethods.ShowWindow(HandleRef hWnd, Int32 nCmdShow) at System.Windows.Forms.Control.SetVisibleCore(Boolean value) at System.Windows.Forms.Form.SetVisibleCore(Boolean value) at System.Windows.Forms.Control.set_Visible(Boolean value) at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.Run(ApplicationContext context) at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun() at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel() at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine) at TestApp.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() Edited March 7, 2009 by mooman_fl Quote "Programmers are tools for converting caffeine into code." Madcow Inventions -- Software for the Sanity Challenged.
Administrators PlausiblyDamp Posted March 8, 2009 Administrators Posted March 8, 2009 My bad, I was fiddling with a couple of other things and forgot to say. I think the problem is down to where you are loading the plugins from - all the files are found in the same folder as the executable and a "plugins" subfolder, IIRC this will cause problems as the same interfaces / classes are being loaded more than once. Try getting it so the plugins and supporting files only exist once in either one or the other of those two places and see if that fixes the problem. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
mooman_fl Posted March 8, 2009 Author Posted March 8, 2009 (edited) That makes sense. Guess I wasn't stuck on stupid, just on ignorant. :o EDIT: Ok, I got it worked out temporarily so I can continue to progress by just having everything compile to the same directory. You were right and that seemed to have solved the problem. Since I tend to do my unit testing as I go, the parts that deal with using a specific plugin directory were already tested and found to work. :cool: I am assuming that what was happening is that the plugins were copying the interface assemblies to the plugin directory because they are dependencies. Ideally I would like to stop this from happening so that I can continue to develop and test as the component would typically be used. So if you have any ideas, I am open to suggestion. :D EDIT (AGAIN): WOOOOOT!!!!! Got all the basic functionality of the control added. It will now load plugins successfully. You still have to do a DirectCast in your app's code at the point that you want it to load plugins, but that is unavoidable since the control operates with no knowledge of the plugin interfaces you might want to use. There are some bells and whistles still to add, and a few things that will make it even more flexible... but at least it is working. The code within an app for loading in which plugins are available, then loading a particular one and using it looks like this: 'this will be our usable plugin Dim plugTest As IStrings.IProcessString 'This does all the grunt work. 'It determines available plugin assemblies, and loads info on them. 'It does this without retaining a loaded assembly in memory. PowerPlug1.StartPluginEngine() 'This loads the first plugin assembly in the list. PowerPlug1.Plugins.Item(0).Load() 'This makes the plugin reference plugTest = DirectCast(PowerPlug1.Plugins.Item(0).PluginObjects.Item(0).Plugin, IStrings.IProcessString) 'This uses it. MessageBox.Show(plugTest.StringOperation("This is a test.")) 'This result in this instance reverses the string. I could still use an answer to the above question though, since I still have a bit of work to go. Again, thanks for all the help. Truely! Edited March 8, 2009 by mooman_fl Quote "Programmers are tools for converting caffeine into code." Madcow Inventions -- Software for the Sanity Challenged.
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.