Running into a Pythonnet error on a .NET assembly with ComVisible(false)

396 views Asked by At

I hit a bit of snag after setting up my new laptop and running code that I was already using for some years.

My setup is that I run a python script that helps executing some repetitive stuff in our software via the developers SDK API.

The software setup is:

  • Windows 10 64 bit
  • Python 3.11.6
  • pythonnet 3.0.1

I run the following code from my library handling all the dll stuff:

import sys
import clr  # #                          Python.NET library

clr.AddReference("System")  # #          Import via pythonnet the .NET System Library
clr.AddReference("System.Reflection")  # Import via pythonnet the .NET System.Reflection Library
import System  # #                       Import the .NET library into Python environment
import System.Reflection  # #            Import the .NET library into Python environment
from System import Array, Double  # #    Use the .NET libraries in Python

# SDK dll
basepath = r"C:\folder1\folder2"
sdk_dll_file = os.path.join(basepath, r"Interop.AbSDK.dll")
sdk_dll = System.Reflection.Assembly.LoadFile(sdk_dll_file)
sdk_class_type = sdk_dll.GetType("AbSDK.AbSDKClass")
Sdk = System.Activator.CreateInstance(sdk_class_type)
Connected = Sdk.Connect("127.0.0.1")
if not Connected:
    raise IOError("Connection to SDK failed!")

Then the script runs this line (The line 37 in the error below):

answer_act = ab.ask_for_string_pulldown("Select the Active Collection:", DATA_COLLECTIONS)

Which executes a bit of code in the library (The line 262 and 263 in the error below):

QvStringList = System.Runtime.InteropServices.VariantWrapper(questionList)
Sdk.SetStringRefListArg("Question or Statement", QvStringList)

Which throws this error:

Traceback (most recent call last):
File "c:\folder1\folder2\script.py", line 37, in <module>
  answer_act = ab.ask_for_string_pulldown("Select the Active Collection:", DATA_COLLECTIONS)
File "C:\folder1\folder2\lib.py", line 263, in ask_for_string_pulldown
  Sdk.SetStringRefListArg("Question or Statement", QvStringList)
System.InvalidOperationException: This type has a ComVisible(false) parent in its hierarchy, therefore QueryInterface calls for IDispatch or class interfaces are disallowed.      
 at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters)   
 at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
 at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)
 at AbSDK.AbSDKClass.SetStringRefListArg(String argName, Object& stringList)

I run Visual Studio with the software's SDK sample, just opening the Visual Studio project provides me with the "Interop.AbSDK.dll" in the "obj/Debug" folder of the project which has always been enough for me.

But now suddenly I ran into this error.

Googling for it lead me to the assembly settings in Visual Studio in the file "AssemblyInfo.cs" where I changed this line from false to true:

[assembly: ComVisible(true)]

But sadly no changes.

Do I need to set this "ComVisible(true)" somewhere in the Python script?

Edit 1

After user 'LOST' made a fair point about not knowing which .net version is running the code, I tried to get some more info.

Running pythonnet.get_runtime_info() in the python code right after import clr, provides no usefull info:

    Runtime: .NET Framework
    =============
    Version:      <undefined>
    Initialized:  True
    Shut down:    False
    Properties:
    domain = 
    config_file = None

Forcing pythonnet.load("coreclr") before import clr provides more usefull info:

    CoreCLR
    =============
    Version:      <undefined>
    Initialized:  True
    Shut down:    False
    Properties:
    FX_DEPS_FILE = C:\Program Files\dotnet\shared\Microsoft.NETCore.App
    TRUSTED_PLATFORM_ASSEMBLIES = C:\Program Files\dotnet\shared\Micros
    NATIVE_DLL_SEARCH_DIRECTORIES = ;C:\Program Files\dotnet\shared\Mic
    PLATFORM_RESOURCE_ROOTS = ;
    APP_CONTEXT_BASE_DIRECTORY = 
    APP_CONTEXT_DEPS_FILES = C:\Program Files\dotnet\shared\Microsoft.N
    PROBING_DIRECTORIES = 
    RUNTIME_IDENTIFIER = win10-x64
    System.Reflection.Metadata.MetadataUpdater.IsSupported = false

The first code provides this error:

System.InvalidOperationException: This type has a ComVisible(false) parent in its hierarchy

The second way provides a slightly different error:

System.Runtime.InteropServices.COMException:  (0x0000F108): Internal application error.

I narrowed it down to the System.Runtime.InteropServices.VariantWrapper, is this variantwrapper somehow causing this now, due to different versions?

Edit 2

After digging deeper I found that the above mentioned Python method ab.ask_for_string_pulldown() calls a C# method, which has 2 variables as input, a string and a list of strings, the second one is causing the problem, it somehow expects it as a reference:

SDK.SDKClass.SetStringRefListArg(String argName, Object& stringList)

The String Argname is fine and not discussed, the Object& stringList is setup as follows:

QvStringList = List[String]()
QvStringList.Add("the string here")
Sdk.SetStringRefListArg("Question or Statement", QvStringList)

At the last line (277) the error is thrown:

      File "C:\foo\PyLib.py", line 277, in ask_for_string_pulldown
    Sdk.SetStringRefListArg("Question or Statement", QvStringList)
System.Runtime.InteropServices.COMException:  (0x80020005): Type mismatch. (0x80020005 (DISP_E_TYPEMISMATCH))
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)   
   at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Object[] aArgs, Boolean[] aArgsIsByRef, Int32[] aArgsWrapperTypes, Type[] aArgsTypes, Type retType)   
   at SDK.SDKClass.SetStringRefListArg(String argName, Object& stringList)
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
1

There are 1 answers

4
LOST On

Not an answer, but a couple of debugging hints:

  1. Check the class hierarchy and find which class violates the requirement.
  2. Check what kind of runtime you are loading, e.g. .NET Framework 4.x or .NET 6 or later.