Skippy Fastol
Skippy Fastol

Reputation: 1775

How to call a method located inside a C# DLL, from VB.Net, with late binding

I can't get a grip on how the various "Init", "Accumulate" ... methods work, to enable calling a method located inside a DLL, from VB.Net code.

Let's say the method to call has the following signature :

public double ComputeMeanPosition(ref SortedList<DateTime, double> posByTime)

Would you please kindly point me to an actual example of the use of the methods, or simply give me a few hints as how to actually pass the parameters to the method, call it and fetch the results ?

@Olivier Jacot-Descombes: I definitely prefix the class name by the namespace name, but yet fail to be able to reach the object. In fact I am surprised that your kind suggestion does not involve the following methods, displayed through introspection of the loaded DLL :

Type: MyClassName
        Method: Void Init()
        Method: Void Accumulate(System.Data.SqlTypes.SqlDouble, System.Data.SqlTypes.SqlDateTime, System.Data.SqlTypes.SqlBoolean)
        Method: Void Merge(MyClassName)
        Method: System.Data.SqlTypes.SqlDouble Terminate()
        Method: Void Write(System.IO.BinaryWriter)
        Method: Void Read(System.IO.BinaryReader)
        Method: Boolean Equals(System.Object)
        Method: Int32 GetHashCode()
        Method: System.String ToString()
        Method: System.Type GetType()

EDIT

In fact I have a code similar to the following one, that successfully manages to inspect the DLL and gets several types and methods out of it, giving the following results above for the method I would like to call.

Here is the code

        For Each oneModule As Reflection.Module In useAssembly.GetLoadedModules()
            Console.WriteLine("   - " & oneModule.Name)
            For Each oneType As System.Type In oneModule.GetTypes()
                Console.WriteLine("     Type: " & oneType.Name)
                For Each oneField As Reflection.FieldInfo In oneType.GetFields()
                    Console.WriteLine("        Field: " & oneField.ToString())
                Next oneField

                For Each oneMethod As Reflection.MethodInfo In oneType.GetMethods()
                    Console.WriteLine("        Method: " & oneMethod.ToString())

                    [[ ADD "Invoke" here ?]]
                Next oneMethod
            Next oneType
        Next oneModule

At the end, it seems that the [[...]] is at the place where the Invoke method should be called to call the method of my choice, but that is where I'm stuck... Would I need to build an object before calling it ? How should I pass it the parameters ? How to get the result ?

Upvotes: 1

Views: 9383

Answers (3)

Icemanind
Icemanind

Reputation: 48686

Something like this should work:

using System;
using System.Reflection;
using System.IO;

public class MainClass
{
      public static int Main(string[] args)
      {
           Assembly a = null;
           try
           {
                a = Assembly.Load("YourLibraryName");
           }
           catch(FileNotFoundException e)
               {Console.WriteLine(e.Message);}

           Type classType = a.GetType("YourLibraryName.ClassName");

           object obj = Activator.CreateInstance(classType);

           object[] paramArray = new object[1];    
           paramArray[0] = new SortledList<DateTime, double>();
           MethodInfo mi = classType.GetMethod("ComputeMeanPosition");
           mi.Invoke(obj, paramArray);

           return 0;
       }
 }

For VB.NET:

Imports System
Imports System.Reflection
Imports System.IO

Public Class MainClass
    Public Shared Function Main(args As String()) As Integer
        Dim a As Assembly = Nothing
        Try
            a = Assembly.Load("YourLibraryName")
        Catch e As FileNotFoundException
            Console.WriteLine(e.Message)
        End Try

        Dim classType As Type = a.[GetType]("YourLibraryName.ClassName")

        Dim obj As Object = Activator.CreateInstance(classType)

        Dim [paramArray] As Object() = New Object(0) {}
        [paramArray](0) = New SortledList(Of DateTime, Double)()
        Dim mi As MethodInfo = classType.GetMethod("ComputeMeanPosition")
        mi.Invoke(obj, [paramArray])

        Return 0
    End Function
End Class

Tested and this worked:

 Dim a As Assembly = Nothing
    Try
        a = Assembly.Load("TestLib")
    Catch ex As FileNotFoundException
        Console.WriteLine(ex.Message)
    End Try

    Dim classType As Type = a.[GetType]("TestLib.Class1")

    Dim obj As Object = Activator.CreateInstance(classType)

    Dim [paramArray] As Object() = New Object(0) {}
    [paramArray](0) = New SortedList(Of DateTime, Double)()
    Dim mi As MethodInfo = classType.GetMethod("ComputeMeanPosition")
    mi.Invoke(obj, [paramArray])

Upvotes: 7

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112259

You can call this method as if was declared like this in VB

Public Function ComputeMeanPosition( _
    ByRef posByTime As SortedList(Of DateTime, Double)) As Double

Note: I used this online snippet converter to convert the C# code to VB. You have to add an empty method body {} to the C# method header before converting it.

public double ComputeMeanPosition(ref SortedList<DateTime, double> posByTime) {}

As this method is not declared as static in C# (Shared in VB) you first need to create an object. The method is most likely declared inside a class. Let's say the class was named "MyClass", then you would have to write something like this

Dim posByTime = New SortedList(Of DateTime, Double)()
Dim obj = New MyClass()
Dim result As Double = obj.ComputeMeanPosition(posByTime)

If the constructor (the New method) declares arguments, you will have to pass them when creating the object

Dim obj = New MyClass(arg1, arg2, ...)

Depending on what ComputeMeanPosition expects, you will have to add items to the list posByTime before calling it.


If the method was declared as static in C# (Shared in VB), you would qualify it with the class name, instead of creating an object.

Dim posByTime = New SortedList(Of DateTime, Double)()
Dim result As Double = MyClass.ComputeMeanPosition(posByTime)

UPDATE

If you load the assembly dynamically you will probably do something like this

Dim ass As Assembly = Assembly.LoadFrom("C:\SomePath\MyDll.dll")
Dim obj As Object = ass.CreateInstance("MyClass", True) 

Dim posByTime = New SortedList(Of DateTime, Double)()
Dim result As Double = obj.ComputeMeanPosition(posByTime)

And you will have to set the Option Strict Off for late binding.


If the constructor requires arguments, you will have to pass them to another overload of CreateInstance

Dim args = new Object() { arg1, arg2, ... }
Dim obj As Object = ass.CreateInstance("MyClass", True, _
    BindingFlags.Public Or BindingFlags.Instance, Nothing, Nothing, _
    args, Nothing, Nothing)

Upvotes: 1

Jaimal Chohan
Jaimal Chohan

Reputation: 8645

This is not the actual code, because I'm not in sitting in front of an actual c# editor, but something like this

// using System.Reflection required

// first load the external assembly into the appdomain and 
// create an instance of the object
var assembly = Assembly.Load("Path to the Assembly");
var type = assembley.GetType("TheNameOfTheClass");
var ctor = type.GetConstuctor();
var object = ctor.Invoke(); // assuming an empty construtor, else you'll need to pass in data

// next invoke the method
var methodInfo = assembly.GetMethodByName("ComputeMeanPosition");
var param = new SortedList<DateTime, double>();
var result = methodInfo.Invoke(object, new object[] { param });

Upvotes: 2

Related Questions