【问题标题】:VB6/COM Interop: where do these events come from?VB6/COM 互操作:这些事件从何而来?
【发布时间】:2016-03-27 10:44:25
【问题描述】:

我在 C# 4.0 中编写了一个 COM 可见的类库,我正在使用 VB6。事情有效,只有当我打开 VB6 对象浏览器 并查看暴露的成员时,我才能看到每个暴露成员的事件...... 但是 C# 代码没有定义它们中的任何一个

这正常吗?我做错了吗?

[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyClass))]
public class MyClass : IMyClass
{
    public void DoSomething(string someParam)
    {
        ...
    }
}

public interface IMyClass
{
    void DoSomething(string someParam);
}

程序集使用强名称密钥签名,AssemblyInfo.cs 具有 [assembly: ComVisible(true)] 属性集,但我不确定它与问题有什么关系。

当我查看 VB6 中的对象浏览器时,我希望看到 DoSomething(string)MyClass 的成员,我确实这样做了,但是我也是 /em> 为每个公开的方法看到一个具有匹配签名的事件,例如 Event DoSomething(someParam As String) 作为 MyClass 的成员。

更令人费解的是(至少对我而言),属性也有一个“匹配”事件(虽然只能从小闪电图标看出)——如果MyClass 定义了这样的属性:

public string SomeProperty { get; set; }

VB6 对象浏览器会说“事件”被定义为Property SomeProperty As String,这让我大吃一惊——“属性”是如何 1) 被复制的,2) 复制品如何在对象浏览器?这同样适用于 get-only 属性,它们具有只读的“属性/事件”对应项。

这些事件是从哪里来的?如何消除它们?

更新一张图片值一千字:

更新错误ComSourceInterfaces 属性被错误地用来代替ComDefaultInterface 属性。将前者换成后者会得到预期的结果:

【问题讨论】:

  • 鉴于这本质上是一个错字,我将投票关闭它,因为它过于本地化。我建议删除它。
  • @Deanna 确实错字了,但我确实学到了一些关于将事件暴露给 COM 的知识。我想你是对的,肯定有很多关于将 .net 事件暴露给 COM 的帖子......除了这里有两个高质量的帖子不应该被删除:)
  • @Deanna 我认为我们不应该删除它。这是一个错字,但它可能会使其他 COM 互操作新手感到困惑。我也从中学到了一些东西。
  • @MarkJ 你们都说得很好:)

标签: c# vb6 com-interop


【解决方案1】:

通过将typeof(IMyClass) 作为参数传递给 ComSourceInterface 属性,您是在说 IMyClass 中的所有内容都是一个事件。

如果您不希望类的事件接口删除 ComSourceInterface 属性。

如果您确实想要将 C# 类中的事件公开给 VB,请执行以下操作:

当您创建一个 COM 可见类时,您还需要创建一个只为您的类定义事件处理程序的接口。您的类应该用 COMSourceInterface 来装饰,指定您的事件处理程序接口,并且应该定义您的事件并实现事件处理程序接口。另一个例子见How To: Raise Events Handled by a COM sink

[GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface MyEvents
{
    void ConnectedEvent(string state);
}


[ComSourceInterfaces(typeof(MyEvents))]
public class MyClass
{
    public event Action<string> ConnectedEvent;

    public MyClass() { }

    public void DoSomething(string state)
    { 
        if (ConnectedEvent != null)
            ConnectedEvent(state);
    }
}

另见:Murat's Corner: Exposing COM Events

【讨论】:

  • 我从来没有向 COM 公开过 C# 事件,感谢这篇文章,当我需要这样做时,我一定会回来的,...但我不能接受它作为答案因为 C# 代码中没有任何事件...问题是关于这些 COM/VB6 事件到底是从哪里来的;我不认为对 this 问题的 this 回答(原样)给予支持。但是,如果我询问是否将 C# 事件暴露给 COM/VB6,这将是一个很棒的答案;如果您要编辑您的答案,请通过附加来编辑您的帖子;它仍然是有价值的信息,虽然有点跑题了。
  • @retailcoder 但您使用了ComSourceInterfaces 属性。这告诉 tlbexport 您的对象可以在给定接口的情况下获取事件。
  • @retailcoder 我的回答暗示您已经使用 ComSourceInterface 属性指定了一个事件接口。如果您不想暴露事件连接点,请将其删除。
  • 仅供参考,第一个链接已失效:)
【解决方案2】:

您基本上会发现 COM 中的事件并没有什么特别之处。像 COM 中的任何东西一样,事件由接口支持。指定事件方法的接口的唯一特殊之处在于它在类型库中用 [source] 属性标记。这就是 [ComSourceInterfaces] 属性所做的一切,在生成类型库时被 Tlbexp.exe 识别。

COM 中的属性也没有什么特别之处。它们的工作方式与在 .NET 中的工作方式类似,它们是通过 方法 实现的。一个 getter 和一个 setter 方法。

因此,VB6 会查看您的类型库,并且对具有事件的类感到满意,因为它具有带有 [source] 属性的接口。并且很高兴该接口具有方法,它们可以拥有的所有方法,因此它假定这些方法是在引发事件时运行的方法。认识到这些方法也是属性的访问器是不够聪明的,它假定类型库作者知道他在做什么。

事件在 COM 中称为“连接点”。谷歌 IConnectionPoint 了解更多信息。如果您曾经使用自定义事件访问器创建过 WinRT 组件,那么您还会发现 COM 事件与 .NET 事件几乎没有共同之处。

Anyhoo,解决方法很简单,只在引发事件时使用 [ComSourceInterface]。

【讨论】:

  • 当类派生自泛型类型时,...这就是为什么 ComSourceInterfaceAttribute...没想到会出现这种“副作用”!感谢您的解释,我今天学到了一些东西!
  • COM 不支持泛型,只有没有类型参数的具体类可以是 [ComVisible]。
  • 啊!这是我的错误:我混淆了[ComSourceInterface][ComDefaultInterface] - “类型库导出器遇到了一个派生自泛型类型并且没有标记为 [...] ClassInterfaceType.None [...] 考虑 [. ..] 使用 [ComDefaultInterface] 属性将显式接口公开为 COM 的默认接口。” ...我知道 COM 不支持泛型,我写了“当一个类派生自一个泛型类型”。
猜你喜欢
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 2011-02-01
  • 2012-03-03
  • 2012-10-15
  • 1970-01-01
相关资源
最近更新 更多