将 .NET 事件公开给 COM?

2024-04-08

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

作为记录,我检查了其他问题,但没有帮助。

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

[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();
}

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

事件命名注意事项:
C#类中定义的事件名称和接口上定义的接口方法名称必须相同。在这个例子中IEvents::OnDownloadCompleted对应于DemoEvents::OnDownloadCompleted
.

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

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

using System;
using System.Diagnostics;
using System.EnterpriseServices;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace COMVisibleEvents
{
    [ComVisible(true)]
    [Guid("8403C952-E751-4DE1-BD91-F35DEE19206E")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IEvents
    {
        [DispId(1)]
        void OnDownloadCompleted();

        [DispId(2)]
        void OnDownloadFailed(string message);
    }

    [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();
        public delegate void OnDownloadFailedDelegate(string message);

        public event OnDownloadCompletedDelegate OnDownloadCompleted;
        public event OnDownloadFailedDelegate OnDownloadFailed;

        private string FileNamePath(string filename) 
            => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), filename);

        public async Task DownloadFileAsync(string address, string filename)
        {
            try
            {
                using (var webClient = new WebClient())
                {
                    await webClient
                        .DownloadFileTaskAsync(new Uri(address), FileNamePath(filename))
                        .ContinueWith(t =>
                        {
                            if (t.Status == TaskStatus.Faulted)
                            {
                                var failed = OnDownloadFailed;
                                failed?.Invoke(GetExceptions(t));
                            }
                            else
                            {
                                var completed = OnDownloadCompleted;
                                completed?.Invoke();
                            }
                        }, TaskScheduler.FromCurrentSynchronizationContext());
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }

            #region Local

            string GetExceptions(Task task)
            {
                var innerExceptions = task.Exception?.Flatten().InnerExceptions;
                if (innerExceptions == null)
                    return string.Empty;
                var builder = new StringBuilder();
                foreach (var e in innerExceptions)
                    builder.AppendLine(e.Message);
                return builder.ToString();
            }

            #endregion Local
        }
    }
}

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

注册 COM 互操作

在本地计算机上的开发过程中使用此设置。https://stackoverflow.com/a/3700057/863240 https://stackoverflow.com/a/3700057/863240

VBA 客户端参考*.tlb file

添加参考*tlb这是由生成的regasm。这里是这个的名字tlb文件是COMVisibleEvents.

这里使用 Excel 用户表单作为 VBA 客户端。单击按钮后,该方法DownloadFileAsync被执行,当该方法完成时,事件被处理程序捕获m_eventSource_OnDownloadCompleted。在此示例中,您可以从 datahub.io 下载机场代码。

VBA 客户端代码 (MS Excel 2016)

Option Explicit

Private WithEvents m_eventSource As DemoEvents

Private Sub DownloadFileAsyncButton_Click()
    m_eventSource.DownloadFileAsync "https://datahub.io/core/airport-codes/r/airport-codes.json", "airport-codes.json"
End Sub

Private Sub m_eventSource_OnDownloadCompleted()
    MsgBox "Download completed..."
End Sub

Private Sub m_eventSource_OnDownloadFailed(ByVal message As String)
    MsgBox "Download failed. " & message, vbCritical, "Error"
End Sub

Private Sub UserForm_Initialize()
    Set m_eventSource = New COMVisibleEvents.DemoEvents
End Sub

Result

源代码也在 GitHub 上:

https://github.com/ddmail/COMVisibleEvents.git https://github.com/ddmail/COMVisibleEvents.git

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 .NET 事件公开给 COM? 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 是否可以强制 XMLWriter 将元素写入单引号中?

    这是我的代码 var ptFirstName tboxFirstName Text writer WriteAttributeString first ptFirstName 请注意 即使我使用 ptFirstName 也会以双引号结束 p
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • 使闭包捕获的变量变得易失性

    闭包捕获的变量如何与不同线程交互 在下面的示例代码中 我想将totalEvents 声明为易失性的 但C 不允许这样做 是的 我知道这是错误的代码 这只是一个例子 private void WaitFor10Events volatile
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • 当操作繁忙时,表单不执行任何操作(冻结)

    我有一个使用 C 的 WinForms 应用程序 我尝试从文件中读取一些数据并将其插入数据表中 当此操作很忙时 我的表单冻结并且无法移动它 有谁知道我该如何解决这个问题 这可能是因为您在 UI 线程上执行了操作 将文件和数据库操作移至另一个
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce

随机推荐

  • 为什么 Objective-C 没有命名空间?

    为什么 Objective C 没有命名空间 这似乎是一个简单的功能 可以使某些类名更具可读性 AVMutableVideoCompositionLayerInstruction有人吗 并删除类名上愚蠢的字母前缀 这主要是因为向后兼容吗 实
  • 集中式和分布式版本控制系统之间的比较[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何让我的 tkinter 原型系统计算出正确的总价

    我是一名初学者 我正在使用 Tkinter 为 DIY 商店创建一个原型系统 作为我任务的一部分 该应用程序的工作原理是填写一份简单的调查来创建订单 单击 输入数据 按钮后 页面底部应显示您的选择结果 包括总价 我需要一些关于如何编写系统计
  • 使用 serde 序列化时如何按字母顺序对字段进行排序?

    我有一个 API 要求对象的字段按字母顺序排序 因为必须对结构进行哈希处理 在 Java Jackson 中 您可以在序列化器中设置一个标志 MapperFeature SORT PROPERTIES ALPHABETICALLY 我在 S
  • 聚合物全局变量

    我正在开发一个 Polymer 应用程序 它从 RESTful API 中提取数据并使用它来构建界面 我在概念上坚持的一个特定领域是单态模式的实现 描述于http www polymer project org docs polymer p
  • BigQueryTable.InsertRows 间歇性地抛出未找到表 xx

    我们只是删除表 创建表 然后使用插入数据 BigQueryTable table try table dataset GetTable tableName table Delete catch finally table dataset C
  • 如何在 .NET Framework 4.7.1 中使用 Azure KeyVault 覆盖 Web.config 值

    如何在 ASP NET Framework 4 7 1 WebForms 应用程序中实现 Microsoft 的 Azure KeyVault 以覆盖中的值web config使用 KeyVault 中的值 我确实看到参考资料表明我们需要最
  • Youtube iOS 播放器帮助程序库无法正常工作

    我尝试实现 youtube ios player helper 库来在我的应用程序中播放视频 我可以准备好播放器并加载视频 并且可以显示起始缩略图 但是 当我尝试播放视频时 却收到以下错误消息 SendDelegateMessage NSI
  • 通过尾指针添加到链表,无需 3 级间接

    我正在开发一个需要实现链表的项目 在开始这个项目之前 我正在回顾创建链表的经典方法 我意识到过去我一直通过head遍历列表直到到达空指针的指针 我发现没有必要这样做 并以涉及的方式实现它tail指针 但我能想到的唯一方法是涉及三重指针或全局
  • Jquery Multiselect:如何知道选择/取消选择哪个值

    我有多选下拉菜单 每当有选择或取消选择时 我都需要获取该值 我正在使用更改事件 但很难确定选择 取消选择了哪个选项 all options var all multiple each function i selected all i se
  • 如何在 C# 中附加 xml 文件?

    我正在为一个简单进程添加跟踪 以进行审计 我将其构建为 exe 并在调度程序中设置为每 10 分钟运行一次 我想让应用程序将结果输出到 xml 文件中 如果文件存在 则打开并向其追加数据 如果不存在 我想创建一个新的 xml 文件 该文件将
  • C/C++ 中的动态位向量

    我正在寻找一个可以使用的现成的 C 或 C 动态位向量 不幸的是 由于各种原因 我目前无法使用 BoostT 库 std bitvector 看起来很有希望 但它是模板化的 所以我无法动态设置位向量的长度 有人可以建议吗 谢谢 您不必仅仅为
  • 安装SSDT(SQL Server数据工具)时出错

    我在安装 SQL Server Data Tools for Visual Studio 2017 过程中遇到问题 我收到以下错误 已翻译 The requested meta file operation is not supported
  • SwiftUI:两个并排列表和一个导航视图出现意外行为

    我的屏幕有两个List并排 在一个NavigationView 布局渲染正确 我可以独立滚动两个列表 问题是 当我滚动第一个列表时 它位于导航栏后面 而没有触发对其应用背景颜色的效果 下面的 gif 展示了正在发生的事情 这是我用于此视图的
  • request.getSession() 在 sendRedirect() 之后创建新会话

    我们正在开发旅行应用程序 通过这个航班 可以预订酒店 巴士票 它是基于产品的应用程序 我们有近 25 个客户 3 个客户正在使用 其余仍在开发中 我们为所有客户维护一台服务器 一台服务器 应用程序 多个客户端是我的应用程序中的伟大成就 但问
  • 使用 jq 将 JSON 对象转换为 Prometheus 指标格式

    考虑一个 JSON 对象 例如 foo 42 baz 12 bar label1 value1 12 34 建造者jq https github com stedolan jq使用一些数据源 实际的键名称及其数量可能会有所不同 但结果将始终
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • 在Python中的同一个图上绘制多个位置的年度数据[重复]

    这个问题在这里已经有答案了 我有几个站 20 年的数据框 我想以某种方式绘制它 x 轴是年份 y 轴是观测值 线条的颜色显示位置 station year observations 0 3939 2000 0 346518 1 3939 2
  • 通过 UDF 获取 Google 搜索第一个结果[重复]

    这个问题在这里已经有答案了 Santosh 对以下问题有一个很棒的答案 vba 代码 link https stackoverflow com questions 17495644 using vba in excel to google
  • 将 .NET 事件公开给 COM?

    我一直在尝试向 VBA 客户端公开并触发事件 到目前为止 在 VBA 客户端 事件已公开 并且我看到方法事件处理方法已添加到我的模块类中 但是 VBA 事件处理方法不会触发 由于某种原因 调试时事件为空 同步修改我的代码也没有帮助 作为记录