Casting Exception problems.

mooman_fl

Centurion
Joined
Nov 3, 2002
Messages
193
Location
Florida, USA
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:

Visual Basic:
    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.
 
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.
 
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:

Visual Basic:
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:

Visual Basic:
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?
 
Last edited:
A type that implements an interface is different from the interface itself, you cannot cast between two different types. e.g.
Visual Basic:
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.
Visual Basic:
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.
 
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.
 

Attachments

Last edited:
After a couple of changes it now seems to run without any errors.

In the gateway.vb file I changed the following
Visual Basic:
'Delegate declaration to
 Public Delegate Function _GetPluginInformation(ByVal FullPath As String) As IPlug.IPlugInfo
and the GetPluginInfo method to
Visual Basic:
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
 
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()
 
Last edited:
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.
 
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:

Visual Basic:
'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!
 
Last edited:
Back
Top