0

I'm trying to call the method 'MyMethod' of class 'CMyClass'. This method has a parameter of type "CBaseClass", and I'm passing an object of type "CDerivedClass".

Class CBaseClass
    Public m_AMember As String
    Sub MethodOne()
        // DoSomething
    End Sub
End Class

Class CDerivedClass
    Inherits CBaseClass
    Public m_MyMember As Integer
    Sub MethodTwo()
        // DoSomething
    End Sub
End Class

Class CMyClass
    Sub MyMethod(ByVal obj As CBaseClass)
        // DoSomething
    End Sub
End Class

Sub Main()
    'load assembly
    Dim objAssembly As Assembly = Assembly.LoadFrom("myfile.dll")

    'create class instance and MethodInfo object
    Dim t As Type = objAssembly.GetType("MyNamespace.CMyClass")
    Dim obj As Object = Activator.CreateInstance(t)
    Debug.Assert(obj IsNot Nothing)
    Dim m As MethodInfo = t.GetMethod("MyMethod")
    Debug.Assert(m IsNot Nothing)

    'Init arguments (only one)
    Dim par As New CDerivedClass()
    Dim parameters As Object() = New Object(0) {par}

    'invoke method
    m.Invoke(obj, parameters) '<<<< ArgumentException here!
End Sub

The argument exception says "object of type 'MyNamespace.CDerivedClass' cannot be converted to type 'MyNamespace.CBaseClass'.

I changed "ByRef" to "ByVal" in MyMethod signature, but nothing changed. I tried to change type of 'par' object with:

Dim par As CBaseClass = New CDerivedClass()

without success. How I can invoke correctly the method "MyMethod" with an instance of derived class? Thank you very much.

Fil
  • 1,032
  • 13
  • 29
  • 1
    You are probably battling loading context, try Load() instead of LoadFrom(). – Hans Passant May 21 '15 at 17:31
  • Maybe you're right? With an array of bytes nothing changed, but with Load("ConsoleApplication1") it runs (source from @Kratz answer)... But in my original project the dll is in another path respect to the exe and it doesn't find it... I've search on internet but I don't know how to use Load(AssemblyName). I have many dll in a certain path (eg. "c:\my_dlls") instead my calling project is in another path (eg. "c:\myProjects\MyCallingProject\bin\debug\MyCallingProject.exe). – Fil May 21 '15 at 18:38
  • Hmya, intentionally invoking DLL Hell is a very drastic mistake. Use the AppDomain.AssemblyResolve event to help the CLR find DLLs stored in odd places. – Hans Passant May 21 '15 at 18:43
  • In my project I have N library in a path. I don't know their names, nor the information of their AssemblyNames. Before the instruction that load the assembly I have a for loop reading all files found in library path specified. – Fil May 21 '15 at 20:24

2 Answers2

0

I just tried your code in a stand alone project and it worked fine. I only adjusted the assembly line to get the current assembly instead of from a file, and I changed the name in GetType to reflect my namespace. My guess is that perhaps your DLL you are loading is out of date, or you have duplicate definitions of the classes in your calling assembly, and thats why it cannot do the type conversion.

        Imports System.Reflection

    Module Module1

        Sub Main()
            'load assembly
            Dim objAssembly As Assembly = Assembly.GetExecutingAssembly()

            'create class instance and MethodInfo object
            Dim t As Type = objAssembly.GetType("ConsoleApplication1.CMyClass")
            Dim obj As Object = Activator.CreateInstance(t)
            Debug.Assert(obj IsNot Nothing)
            Dim m As MethodInfo = t.GetMethod("MyMethod")
            Debug.Assert(m IsNot Nothing)

            'Init arguments (only one)
            Dim par As New CDerivedClass()
            Dim parameters As Object() = New Object(0) {par}

            'invoke method
            m.Invoke(obj, parameters) '<<<< No ArgumentException here!
        End Sub

    End Module

    Class CBaseClass
        Public m_AMember As String
        Sub MethodOne()

        End Sub
    End Class

    Class CDerivedClass
        Inherits CBaseClass
        Public m_MyMember As Integer
        Sub MethodTwo()

        End Sub
    End Class

    Class CMyClass
        Sub MyMethod(ByVal obj As CBaseClass)

        End Sub
    End Class
Kratz
  • 4,280
  • 3
  • 32
  • 55
  • Thanks for trying my code. I tried yours and it's ok, since it uses the same assembly. Starting from that project, I added 'public' in class definitions, changed Assembly initialization loading from "myfile.exe". I compiled the project and made a copy of ConsoleApplication1.exe renamed to "myfile.exe". Launching the app I have the same ArgumentException :( – Fil May 21 '15 at 18:02
  • 1
    Out of curiosity, what if you changed your argument to load from the same assembly like `Dim par As Object = Activator.CreateInstance(objAssembly.GetType("MyNamespace.CDerivedClass"))` ? – Kratz May 21 '15 at 18:18
  • It works :'( So it makes confusion between CDerivedClass of calling project and CDerivedClass of called DLL (even if it's the same file shared between the two projects and namespace is the same)? – Fil May 21 '15 at 18:33
0

Finally I solved using serialization...

So 'par' is the string containing the serialized object of type CDerivedClass in the calling project.

MyMethod is changed to:

MyMethod(xml_CBaseClass As String)

In dll project the string parameter xml_CBaseClass is deserialized creating an object of CBaseClass.

Note: since I have derived type, Deserialization of derived class give another problem. The solution is https://stackoverflow.com/a/590711/1315873 (I just made a little change, using StringWriter for serialization, StringReader for deserialization, instead of using MemoryBuffer).

CBaseClass has fixed derived types so I wrote them hard-coded, but to be flexible you can do something like:

    Dim subTypes as New List(Of Type) '<- all classes derived from 'myType'
    For Each t In myType.Assembly.GetTypes()
        If t.IsSubclassOf(myType) Then
            subTypes.Add(t)
        End If
    Next

CBaseClass and all its derivated classes must have constructor New() without parameters.

I load assemblies using LoadFrom() since I don't know their names (I use Dir() to get all them from a known fixed folder).

Community
  • 1
  • 1
Fil
  • 1,032
  • 13
  • 29