Search code examples
pythonc#.net-corecomlanguage-interoperability

Interoperability issue with COM events in Python and a COM object created in .NET Core 6.0


I am currently trying to create a COM object that is made in C# in .NET Core 6.0. The COM object itself works as intended. The issue that I am having is related to the custom events that the COM object contains. I have tested Excel as a client and there the events would work fine. When I try to use the COM object with its events in Python though it won't work.

I tried it like this:

import win32com
from win32com.client import DispatchWithEvents
import pythoncom
import pywintypes

class ComEvents():
    def OnAdditionDone():
        print('Addition is done.')

unk = pythoncom.CoCreateInstance(pywintypes.IID('ComplexComObject'), None, pythoncom.CLSCTX_ALL, pythoncom.IID_IUnknown)
dispevents = DispatchWithEvents(unk.QueryInterface(pythoncom.IID_IDispatch), ComEvents)

I get the following error message:

  Message=This COM object can not automate the makepy process - please run makepy manually for this object
  Source=C:\PathToProject\PythonEventTesting\PythonEventTesting\PythonEventTesting.py
  StackTrace:

During handling of the above exception, another exception occurred:

  File "C:\PathToProject\PythonEventTesting\PythonEventTesting\PythonEventTesting.py", line 13, in <module> (Current frame)
    dispevents = DispatchWithEvents(unk.QueryInterface(pythoncom.IID_IDispatch), ComEvents)

I am not sure if am trying to access the COM events in python correctly.

I also tried to do it like this:

import win32com.client
from win32com.client import Dispatch
import pythoncom
import pywintypes

EventListener = win32com.client.getevents('ComplexComObject')
class Events(EventListener):
    def OnAdditionDone():
        print('Addition is done.')

unk = pythoncom.CoCreateInstance(pywintypes.IID('ComplexComObject'), None, pythoncom.CLSCTX_ALL, pythoncom.IID_IUnknown)
dispevents = Dispatch(unk.QueryInterface(pythoncom.IID_IDispatch))

Which led to this error:


  Message=NoneType takes no arguments
  Source=C:\PathToProject\PythonEventTesting\PythonEventTesting\PythonEventTesting.py
  StackTrace:
  File "C:\PathToProject\PythonEventTesting\PythonEventTesting\PythonEventTesting.py", line 9, in <module> (Current frame)
    class Events(EventListener):

I am not sure what I am doing wrong or if there is a better approach to consuming COM events in python. Since I am not that proficient with Python, these two approaches were the only ones I was able to find and neither would work for me.

I am not sure if there is an issue inside my C# code, since the events do work for Excel.

The C# COM object code can be found inside this GitHub repository: https://github.com/Spikxzy/ComInDotNETCore

If you want to test the code yourself be sure to edit the following tag inside the ComObjectWithEvents.csproj correctly:

    <PropertyGroup>
        <!--
        Change the following tags so that this path 'C:\$(VSLocation)\Microsoft Visual Studio\$(VSVersion)\VC\Tools\MSVC\$(CLVersion)\bin\Hostx64\x64'
        is valid for your Visual Studio installation (the path should lead to a cl.exe).
        -->
        <VSVersion>2022\Professional</VSVersion>
        <VSLocation>Program Files</VSLocation>
        <CLVersion>14.31.31103</CLVersion>
        <DriveLetter>N:</DriveLetter>
        <KitsVersion>10.0.19041.0</KitsVersion>
    </PropertyGroup>

After building the project be sure to register the generated '*.comhost.dll' file with 'regsvr32' in the command line tool.


Solution

  • This is a correct python code:

    import win32com.client
    from win32com.client import Dispatch
    import pythoncom
    import pywintypes
    
    unk = pythoncom.CoCreateInstance(pywintypes.IID('ComplexComObject'), None, pythoncom.CLSCTX_ALL, pythoncom.IID_IUnknown)
    dispevents = Dispatch(unk.QueryInterface(pythoncom.IID_IDispatch))
    
    class MyEvents:
        def OnAdditionDone(self):
            print('Addition is done.')
    
    EventListener = win32com.client.WithEvents(dispevents, MyEvents)
    
    print(dispevents.Addition(123, 456))
    

    But it won't work as is.

    First thing to do is make sure the type library (.tlb) you prepare is registered. It's not the case in the ComObjectWithEvents project but you can borrow and adapt the code you already have in the ComObjectWithTLBRegistration project.

    If you don't do that you'll get ti = disp._oleobj_.GetTypeInfo() pywintypes.com_error: (-2146234011, 'OLE error 0x80131165', None, None) which is TLBX_W_LIBNOTREGISTERED.

    After that you will get another error: Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. error. This is because your dispinterface is incorrectly defined in C#, it is currently:

    [ComVisible(true)]
    [Guid(AssemblyInfo.ComEventsGuid)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)] // not good
    public interface ComEvents
    {
        [DispId(1)]
        void OnAdditionDone();
    }
    

    while it must be this:

    [ComVisible(true)]
    [Guid(AssemblyInfo.ComEventsGuid)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ComEvents
    {
        [DispId(1)]
        void OnAdditionDone();
    }