Search code examples
pythonc#comlanguage-interoperability

.NET Core 6.0 COM interoperability issues with Python/Octave


I am currently trying to create a COM object in .NET Core 6. To achieve this I have made a class library and edited the project like this:

C#:

Project file (*.csproj):

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <EnableComHosting>true</EnableComHosting>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <PlatformTarget>x86</PlatformTarget>
  </PropertyGroup>
    
</Project>

COM object (*.cs):

using System;
using System.Runtime.InteropServices;

namespace COMNetCore6
{
    [ComVisible(true)]
    [Guid("64D6F9F6-6163-401A-82E6-C941CAF01399")]
    [ProgId("HelloWorldCOMObject")]
    public class ComObject
    {
        public string SayHello() => "Hello world from .NET Core";
    }
}

After building the project I get a *.comhost.dll file which I can add to the registry by using the command 'regsvr32 *.comhost.dll'. The comhost.dll gets registered successfully and it is working for Visual Basic in Excel as intended.

When I try using this COM object in another programming language though it will not work. I have tried using Octave and Python. While Octave only tells me that it wasn't able to create a server, Python gives me this error at least:

Python:

>>> import win32com.client
>>> comobj = win32com.client.Dispatch("HelloWorldCOMObject")
Traceback (most recent call last):
  File "C:\*\AppData\Local\Programs\Python\Python39-32\lib\site-packages\win32com\client\dynamic.py", line 86, in _GetGoodDispatch
    IDispatch = pythoncom.connect(IDispatch)
pywintypes.com_error: (-2147221021, 'Operation unavailable', None, None)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\*\AppData\Local\Programs\Python\Python39-32\lib\site-packages\win32com\client\__init__.py", line 117, in Dispatch
    dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch, userName, clsctx)
  File "C:\*\AppData\Local\Programs\Python\Python39-32\lib\site-packages\win32com\client\dynamic.py", line 106, in _GetGoodDispatchAndUserName
    return (_GetGoodDispatch(IDispatch, clsctx), userName)
  File "C:\*\AppData\Local\Programs\Python\Python39-32\lib\site-packages\win32com\client\dynamic.py", line 88, in _GetGoodDispatch
    IDispatch = pythoncom.CoCreateInstance(
pywintypes.com_error: (-2147467262, 'No such interface supported', None, None)

I have tried to solve this problem by following the steps described in these links (especially the first one) and adapting them to what I needed:

Can I connect a .NET 5 COM interop object with VB6? https://github.com/GregReddick/ComTestLibrary/tree/master/ComTestLibrary1 https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com

Unfortunately none of the approaches I tried worked. I have tried to create an interface for the COM object and use the correct interoperability annotations in C# but the error was still there. As I have said though the basic approach with no interface and barely any annotation from above worked for Visual Basic.

In this case I am trying to use late binding without a TLB (type library) because I need this to work for both late binding (without TLB) and early binding (with TLB) and I decided to start with late binding.

There has been a similar question regarding the late binding approach which wasn't answered as of now: 'No such interface supported' when using .NET 6.0 COM object in python 3.9.

Since the COM object works fine for Visual Basic but doesn't work for other programming languages I am not sure what exactly causes this issue.


Solution

  • The .NET Core wrapper doesn't support this type of call where you ask for IDispatch at the same time you create the COM object:

    C / C++:

    IDispatch* disp;
    CoCreateInstance(YourCLSID, NULL, CLSCTX_ALL, IID_IDispatch, &disp); // fails
    

    Python:

    disp = pythoncom.CoCreateInstance(YourCLSID, None, pythoncom.CLSCTX_ALL, pythoncom.IID_IDispatch) // fails
    

    You must first get an IUnknown reference and QI for IDispatch on it, like this in python (which is what Excel/VBA does):

    import pythoncom
    import pywintypes
    from win32com.client import Dispatch
    
    unk = pythoncom.CoCreateInstance(pywintypes.IID('{64D6F9F6-6163-401A-82E6-C941CAF01399}'), None, pythoncom.CLSCTX_ALL, pythoncom.IID_IUnknown)
    disp = Dispatch(unk.QueryInterface(pythoncom.IID_IDispatch))
    print disp.SayHello
    

    IMHO, python COM support should be modified to do this always...