将.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
结果
- 在给定当前URL的情况下,是否有内置的创建绝对(完全限定)url来自相对路径,例如“〜/ page.aspx”?
- ASP.NET中CORS的问题
- 如何更改UWP的复选框背景?
- 将dll库从Resource加载到当前域(在主exe文件中嵌入dll)
- 为什么Paket安装比Nuget更多的包?
- 为什么FireError在C#2012中失败,但是在VB中工作,而FireInformation在两者中都有效?
- 正则表达式帮助:获取除扩展名.css,.js,.jpg,.gif,.png之外的URL列表
- Revit:在链接模型中设置类型参数
- 无法使用estimatedclosedate在Dynamics CRM“机会”事务类型上执行过滤