将.NET事件暴露给COM?

我一直在尝试向VBA客户端公开和触发事件。 到目前为止,在VBA客户端,事件已暴露,我看到方法事件处理方法添加到我的模块类,但VBA事件处理方法不会触发。 出于某种原因,调试事件时为null。 同步修改我的代码也没有帮助。

为了记录,我已经检查了其他SO问题,但他们没有帮助。

任何好的答案将不胜感激。

[ComVisible(true)] [Guid("56C41646-10CB-4188-979D-23F70E0FFDF5")] [ClassInterface(ClassInterfaceType.None)] [ComSourceInterfaces(typeof(IWebEvents))] [ProgId("MyAssembly.MyClass")] public class MyClass : ServicedComponent, IMyClass { public string _address { get; private set; } public string _filename { get; private set; } [DispId(4)] public void DownloadFileAsync(string address, string filename) { _address = address; _filename = filename; System.Net.WebClient wc = new System.Net.WebClient(); Task.Factory.StartNew(() => wc.DownloadFile(_address, _filename)) .ContinueWith((t) => { if (null != this.OnDownloadCompleted) OnDownloadCompleted(); }); } public event OnDownloadCompletedEventHandler OnDownloadCompleted; } [ComVisible(false)] public delegate void OnDownloadCompletedEventHandler(); [ComVisible(true)] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IWebEvents { [DispId(1)] void OnDownloadCompleted(); } 

对于所有赏金猎人来说,这是一个很好的演出,200个代表点

.net代码中的关键概念是将事件定义为单独接口上的方法,并通过[ComSourceInterfacesAttribute]将其连接到类。 在示例中,这是使用此代码[ComSourceInterfaces(typeof(IEvents))] ,其中IEvents接口定义应在COM客户端上处理的事件。

事件命名注意事项:c#类中定义的事件名称和接口上定义的接口方法名称必须相同。 在此示例中, IEvents::OnDownloadCompleted对应于DemoEvents::OnDownloadCompleted

然后定义第二个接口,它表示类本身的公共API,这里称为IDemoEvents 。 在此接口上定义了在COM客户端上调用的方法。

C#代码(构建到COMVisibleEvents.dll)

 using System; using System.EnterpriseServices; using System.IO; using System.Net; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace COMVisibleEvents { [ComVisible(true)] [Guid("8403C952-E751-4DE1-BD91-F35DEE19206E")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IEvents { [DispId(1)] void OnDownloadCompleted(); } [ComVisible(true)] [Guid("2BF7DA6B-DDB3-42A5-BD65-92EE93ABB473")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IDemoEvents { [DispId(1)] Task DownloadFileAsync(string address, string filename); } [ComVisible(true)] [Guid("56C41646-10CB-4188-979D-23F70E0FFDF5")] [ClassInterface(ClassInterfaceType.None)] [ComSourceInterfaces(typeof(IEvents))] [ProgId("COMVisibleEvents.DemoEvents")] public class DemoEvents : ServicedComponent, IDemoEvents { public delegate void OnDownloadCompletedDelegate(); private event OnDownloadCompletedDelegate OnDownloadCompleted; public string _address { get; private set; } public string _filename { get; private set; } private readonly string _downloadToDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); public async Task DownloadFileAsync(string address, string filename) { try { using (WebClient webClient = new WebClient()) { webClient.Credentials = new NetworkCredential( "user", "psw", "domain"); string file = Path.Combine(_downloadToDirectory, filename); await webClient.DownloadFileTaskAsync(new Uri(address), file) .ContinueWith(t => { if (OnDownloadCompleted != null) { OnDownloadCompleted(); } }, TaskScheduler.FromCurrentSynchronizationContext()); } } catch (Exception ex) { // Log exception here ... } } } } 

文件下载注意事项:要下载文件,请使用WebClient.DownloadFileTaskAsync方法。 它使用任务对象将指定资源作为异步操作下载到本地文件。 任务对象通常在线程池线程上异步执行,而不是在主应用程序线程上同步执行。 因此,有必要在主线程上调用ContinueWith ,否则将无法执行OnDownloadCompleted事件。 这就是使用ContinueWith(continuationAction, TaskScheduler.FromCurrentSynchronizationContext)原因。

regasm

 C:\Windows\Microsoft.NET\Framework\v4.0.30319>regasm C:\Temp\COMVisibleEvents\bin\Debug\COMVisibleEvents.dll /tlb: C:\Temp\COMVisibleEvents\bin\Debug\COMVisibleEvents.tlb 

VBA客户端引用*.tlb文件

添加由regasm生成的*tlb引用。 这里这个tlb文件的名称是COMVisibleEvents

在此处输入图像描述

这里Excel用户表单用作VBA客户端。 单击按钮后,执行了DownloadFileAsync方法,当此方法完成时,事件已在处理程序m_eventSource_OnDownloadCompleted 。 在此示例中,您可以从我的保管箱下载c#项目COMVisibleEvents.dll的源代码。

VBA客户端代码(MS Excel 2007)

 Option Explicit Private WithEvents m_eventSource As DemoEvents Private Sub DownloadFileAsyncButton_Click() m_eventSource.DownloadFileAsync "https://www.dropbox.com/s/0q3dskxopelymac/COMVisibleEvents.zip?dl=0", "COMVisibleEvents.zip" End Sub Private Sub m_eventSource_OnDownloadCompleted() MsgBox "Download completed..." End Sub Private Sub UserForm_Initialize() Set m_eventSource = New COMVisibleEvents.DemoEvents End Sub 

结果

在此处输入图像描述