Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python for .NET: How to explicitly create instances of C# classes using different versions of the same DLL?

I have a .cs file like

namespace SomeNamepace
{


    public struct SomeStruct
    {
        ....
    }

    public static class SomeClass
    {
        ....
    }

So far I use it with PythonNET like

import clr
clr.AddReference('c:\\Test\Module.dll')
from SomeNamepace import SomeClass, SomeStruct

SomeClass.SomeMethod(...)

My problem is now that I need to work with dlls with identical names and no version number set, so PythonNET will not see them as two different dlls but as the same. Even if I import them using the full path with AddReference.

Now I would like to use them explicitly as stated here:

Python for .NET: Using same .NET assembly in multiple versions

like

lib = clr.AddReference('c:\\Test\Module.dll')

I tried a lot of things to create an instance of SomeClass like

lib.SomeNamespace.SomeClass()

or

import System
System.Activator.CreateInstance(lib.GetType('SomeNamespace.SomeClass'))

or using the the methods Initialize or CreateInstance

or as mentioned in the link below

from System import Type
type1 = lib.GetType('SomeNamespace.SomeClass')
constructor1 = type1.GetConstructor(Type.EmptyTypes)    

In the end everything failed, something not found, has no method, etc. etc.

What would be the correct syntax to do this?

like image 355
Joe Avatar asked Apr 20 '18 13:04

Joe


1 Answers

I found an old mailing list conversation that might explain it

Things get a lot more complicated if you need to load more than one version of a particular assembly (or more likely, you have a dependency on some library the does so).

In this case, the names you access via the CLR modules will always come from the first version of the assembly loaded (which will always win in the internals of the runtime).

The solution posted there does not work anymore, I guess the .NET function has been depracated. But there is a solution to this. Instead of using PythonNet you must use the .NET framework directly:

import System

dll_ref = System.Reflection.Assembly.LoadFile(fullPath)
print(dll_ref.FullName)
print(dll_ref.Location)

Check that the correct DLL is used.

To use multiple DLLs with the same version just load it to another variable

another_dll_ref = System.Reflection.Assembly.LoadFile(anotherFullPath)

Now you can use objects from the specified dll.

Instance of a public non-static class

some_class_type = dll_ref.GetType('MyNamespace.SomeClass')
my_instance = System.Activator.CreateInstance(some_class_type)
my_instance.a = 4 # setting attribute
my_instance.b('whatever') # calling methods

Calling a method in a public static class

some_class_type = dll_ref.GetType('MyNamespace.SomeClass')
method = some_class_type.GetMethod('SomeMethod')
# return type and list of parameters
method.Invoke(None, [1, 2.0, '3']) 

Creating a instance of a struct

some_struct_type = dll_ref.GetType('MyNamespace.SomeStruct')
my_struct = System.Activator.CreateInstance(some_struct_type)
my_struct.a = 3

From the docs on LoadFile

Use the LoadFile method to load and examine assemblies that have the same identity, but are located in different paths.LoadFile does not load files into the load-from context, and does not resolve dependencies using the load path, as the LoadFrom method does.LoadFile is useful in this limited scenario because LoadFrom cannot be used to load assemblies that have the same identities but different paths; it will load only the first such assembly.

Methods that don't work:

  • adding references to other DLLs with different versions using dll_ref = clr.AddReference(f) does not work, even if the file name is specified explicitly and dll_ref and Reflection are used to get the methods

  • using a longname (like 'MyNamespace, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null' with System.Reflection.Assembly.Load with the version will still use the first version

  • System.Reflection.Assembly.LoadFrom does not work with multiple version of the same DLL if an assembly with the same identity is already loaded. LoadFrom returns the loaded assembly even if a different path was specified. (Doc LoadFrom)

like image 164
Joe Avatar answered Nov 18 '22 22:11

Joe