Python 中的 COM 事件和在 .NET Core 6.0 中创建的 COM 对象的互操作性问题

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

我目前正在尝试创建一个在 .NET Core 6.0 中用 C# 制作的 COM 对象。 COM 对象本身按预期工作。我遇到的问题与 COM 对象包含的自定义事件有关。我已经作为客户端测试了 Excel 并且事件会正常工作。当我尝试使用 COM 对象及其在 Python 中的事件时,尽管它不起作用。

我这样试过:

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)

我收到以下错误消息:

  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)

我不确定是否正在尝试正确访问 python 中的 COM 事件。

我也试过这样做:

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))

导致此错误的原因:


  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):

我不确定我做错了什么,或者是否有更好的方法来使用 python 中的 COM 事件。由于我对 Python 不是很精通,这两种方法是我唯一能找到的方法,但都不适合我。

我不确定我的 C# 代码是否存在问题,因为事件确实适用于 Excel。

可以在这个 GitHub 存储库中找到 C# COM 对象代码:https://github.com/Spikxzy/ComInDotNETCore

如果您想自己测试代码,请务必正确编辑 ComObjectWithEvents.csproj 中的以下标记:

    <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>

构建项目后,请务必在命令行工具中使用 'regsvr32' 注册生成的“*.comhost.dll”文件。

这是正确的 python 代码:

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))

但它不会按原样工作。

首先要做的是确保您准备的类型库 (.tlb) 已注册。在 ComObjectWithEvents 项目中不是这种情况,但您可以借用和改编您在 ComObjectWithTLBRegistration 项目中已有的代码。

如果您不这样做,您将得到 ti = disp._oleobj_.GetTypeInfo() pywintypes.com_error: (-2146234011, 'OLE error 0x80131165', None, None),即 TLBX_W_LIBNOTREGISTERED。

之后你会得到另一个错误:Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.错误。这是因为您的调度接口在 C# 中定义不正确,目前是:

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

而它必须是这样的:

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