带有 [ComSourceInterfaces] 属性的 C# DLL 在 VBA WithEvents 中不起作用

问题描述 投票:0回答:1

我尝试在 VBA 中使用 C# DLL 以及使用 [ComSourceInterfaces] 属性的事件,但我遇到了问题。这是我到目前为止所拥有的:

C#代码

ITaskRunnerEvents.cs

using System;
using System.Runtime.InteropServices;

namespace ComEventTest
{
    [ComVisible(true)]
    [Guid("c8614250-4291-4fb0-8b45-4aa305b0c595")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ITaskRunnerEvents
    {
        void OnTaskCompleted(string result);
    }
}

TaskRunner.cs

using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace ComEventTest
{
    [ComVisible(true)]
    [Guid("ac9de195-73e8-44ae-8cf1-d8f110421923")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(ITaskRunnerEvents))]
    public class TaskRunner
    {
        public delegate void TaskCompletedEventHandler(string result);
        public event TaskCompletedEventHandler OnTaskCompleted;

        public void RunTask(string input)
        {
            Task.Run(async () =>
            {
                await Task.Delay(5000); // Simulate work
                OnTaskCompleted?.Invoke($"Task completed with input: {input}");
            });
        }
    }
}

VBA代码

类模块:TaskRunnerEventHandler

Option Compare Database
Option Explicit

Public WithEvents taskRunner As ComEventTest.taskRunner

Private Sub taskRunner_OnTaskCompleted(ByVal result As String)
    MsgBox result
End Sub

Public Sub InitializeTaskRunner()
    Set taskRunner = New ComEventTest.taskRunner
End Sub

使用模块

Sub TestTaskRunner()
    Set eventHandler = New TaskRunnerEventHandler
    eventHandler.InitializeTaskRunner
    eventHandler.taskRunner.RunTask "Test Input"
End Sub

问题

  1. 当我在 TestTaskRunner 子例程中运行 eventHandler.taskRunner.RunTask“测试输入”时,出现错误:
Method or data member not found
  1. 我不确定我是否在 C# 代码中正确使用了 [ComSourceInterfaces] 属性。
c# vba events dll com-interop
1个回答
0
投票

最后,我找到了使用 WithEvents 关键字在 VBA 中处理来自

C# COM 对象的事件问题的解决方案。以下是帮助我解决问题的代码和解释:

C#代码:

在 C# 代码中,我实现了一个 COM 对象,该对象在任务完成时引发事件

(OnTaskCompleted)
。关键部分是使用
[ComSourceInterfaces]
属性,它允许 COM 对象将事件公开给 VBA。

TaskRunner.cs

using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace ComEventTest
{
    [Guid("cc6eeac0-fe23-4ce4-8edb-676a11c57c7c")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ITaskRunner
    {
        [DispId(1)]
        void RunTask(string input);
    }

    [Guid("619a141c-5574-4bfe-a663-2e5590e538e2")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ITaskRunnerEvents
    {
        [DispId(1)]
        void OnTaskCompleted(string result);
    }

    [ComVisible(true)]
    [Guid("9acdd19f-b688-48c0-88d9-b81b7697d6d4")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(ITaskRunnerEvents))]
    public class TaskRunner : ITaskRunner
    {
        [ComVisible(true)]
        public delegate void TaskCompletedEventHandler(string result);

        [DispId(1)]
        public event TaskCompletedEventHandler OnTaskCompleted;

        private ConcurrentQueue<string> taskQueue = new ConcurrentQueue<string>();
        private bool isProcessingQueue = false;

        public void RunTask(string input)
        {
            taskQueue.Enqueue(input);
            ProcessQueue();
        }

        private async void ProcessQueue()
        {
            if (isProcessingQueue)
                return;

            isProcessingQueue = true;

            while (taskQueue.TryDequeue(out string input))
            {
                try
                {
                    await Task.Delay(5000); // Simulate work
                    OnTaskCompleted?.Invoke($"Task completed with input: {input}");
                }
                catch (Exception ex)
                {
                    OnTaskCompleted?.Invoke($"Task failed: {ex.Message}");
                }
            }

            isProcessingQueue = false;
        }
    }
}

VBA代码:

在VBA代码中,我使用

WithEvents
关键字来处理
OnTaskCompleted
事件。这允许 VBA 侦听并处理 C# COM 对象引发的事件。我还创建了一个事件处理程序类
(TaskRunnerEventHandler)
来处理事件并处理结果。

类模块:TaskRunnerEventHandler

Option Compare Database
Option Explicit

Public WithEvents taskRunner As ComEventTest.taskRunner

Private Sub taskRunner_OnTaskCompleted(ByVal result As String)
    'MsgBox result
    Debug.Print result
End Sub

Public Sub InitializeTaskRunner()
    Set taskRunner = New ComEventTest.taskRunner
End Sub

Public Sub FireEvent(poraka As String)
    taskRunner.RunTask poraka
End Sub

使用模块

Option Compare Database
Option Explicit

Dim eventHandlers As Collection

Sub InitializeEventHandlers()
    Set eventHandlers = New Collection
End Sub

Sub TestTaskRunner(Optional retr As String)

    If eventHandlers Is Nothing Then
        InitializeEventHandlers
    End If

    Dim newEventHandler As TaskRunnerEventHandler
    Set newEventHandler = New TaskRunnerEventHandler

    newEventHandler.InitializeTaskRunner

    eventHandlers.Add newEventHandler

    Dim i As Integer
    For i = 1 To 10
        newEventHandler.FireEvent "Task " & retr & "-" & i
        Sleep 100 ' Simulate delay for async task running
        Debug.Print "Task " & retr & "-" & i & " is running asynchronously!"
    Next i
    
End Sub

Sub TestTaskRunner_MultCalls()
    ' Fire multiple calls to TestTaskRunner
    Dim i As Integer
    For i = 1 To 10
        Debug.Print "New CALL SUB fire " & i
        TestTaskRunner CStr(i)
        Sleep 500 ' Simulate delay between multiple calls
    Next i
End Sub

说明:

C# COM对象公开了一个事件

(OnTaskCompleted)
,该事件在异步完成任务后触发。

VBA 中,我使用

WithEvents
关键字来声明 COM 对象并捕获
(OnTaskCompleted)
事件。这使我能够在
taskRunner_OnTaskCompleted
方法中处理每个任务的结果。 我还在 VBA 代码中使用
Sleep
模拟了多个任务提交,以延迟执行并为事件的引发和处理提供时间。

这个解决方案有效,现在我可以在 VBA 中无缝处理来自 C# COM 对象的异步事件。

欢迎任何改进上述解决方案的想法!

© www.soinside.com 2019 - 2024. All rights reserved.