我尝试在 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
问题
Method or data member not found
最后,我找到了使用 WithEvents
关键字在 VBA 中处理来自
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 对象的异步事件。
欢迎任何改进上述解决方案的想法!