Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

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.

"Programmers are tools for converting caffeine into code."

 

Madcow Inventions -- Software for the Sanity Challenged.

  • Administrators
Posted

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.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted (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 by mooman_fl

"Programmers are tools for converting caffeine into code."

 

Madcow Inventions -- Software for the Sanity Challenged.

  • Administrators
Posted

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.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted (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 by mooman_fl

"Programmers are tools for converting caffeine into code."

 

Madcow Inventions -- Software for the Sanity Challenged.

  • Administrators
Posted

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

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted (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 by mooman_fl

"Programmers are tools for converting caffeine into code."

 

Madcow Inventions -- Software for the Sanity Challenged.

  • Administrators
Posted

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.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted (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 by mooman_fl

"Programmers are tools for converting caffeine into code."

 

Madcow Inventions -- Software for the Sanity Challenged.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...