【问题标题】:C# exposing to COM - interface inheritanceC# 暴露给 COM - 接口继承
【发布时间】:2010-12-24 04:52:41
【问题描述】:

假设我有一个实现 IBaseClass 的类 BaseClass

然后我有一个接口 IClass 继承了 IBaseClass。

然后我有一个名为 class 的类,它实现了 IClass。

例如:

[ComVisible(true), InterfaceType(ComInterfaceType.IsDual), Guid("XXXXXXX")]
public interface IBaseClass
{
  [PreserveSig]
  string GetA()
}

[ComVisible(true), InterfaceType(ComInterfaceType.IsDual), Guid("XXXXXXX")]  
public interface IClass : IBaseClass
{
  [PreserveSig]
  string GetB()
}

[ComVisible(true), ClassInterface(ClassInterfaceType.None), Guid("XXXXXXX")]
public class BaseClass : IBaseClass
{
  public string GetA() { return "A"; }
}

[ComVisible(true), ClassInterface(ClassInterfaceType.None), Guid("XXXXXXX")]
public class Class : BaseClass, IClass
{
  public string GetB() { return "B"; }
}

当暴露给 COM 时,如果我创建一个“类”实例,它不允许我调用 GetA()。

在 .tlb 文件中查看我的 IDL 时,我的 IClass 界面如下所示:

[
  odl,
  uuid(XXXXXXXXXXXXXXXXXXXXX),
  version(1.0),
  dual,
  oleautomation,

]
interface IClass : IDispatch {
    [id(0x60020000)]
    BSTR GetB();
}

它甚至不像 IClass 派生自 IBaseClass!

如果我取出 IClass 从 IBaseClass 派生的位置并将方法添加到接口中,它就可以工作。

如何让 C# 在 COM 中启用这种继承?当我可以继承它们时,我宁愿不重新实现接口。

废话:检查此链接.Net COM Limitation

如果有人知道为什么会这样,或者比复制粘贴到我的“派生”界面有更好的解决方法,请告诉我。如果没有,我会在几天内标记答案。

【问题讨论】:

  • 您需要查询其他接口才能访问其中的方法。根据您使用对象的语言,它可能不方便。要解决它,您需要为每个类创建一个“平面”接口,或者让编译器使用“ClassInterface”为您完成(还有其他问题)
  • 如果您看到我的链接,回答者提到.Net 在导出时不会将基本接口应用于 COM 接口。所以换句话说,你必须复制粘贴基础接口定义来模拟基础接口。
  • 我认为您应该发布自己的 .Net COM 限制链接作为答案 - 这是正确的解决方案,如果您自己想通了,建议您发布自己的答案。

标签: c# .net inheritance com


【解决方案1】:

这不是 .NET 问题,而是 COM 工作方式的结果。它不支持继承。您将在客户端解决此问题,它需要使用 IBaseClass 的 IID 调用 QueryInterface() 以获取指向 IBaseClass 接口的接口指针,以便它可以调用 GetA()。 .NET 互操作自动提供了一个 QI 实现来实现这一点。但是,在这种情况下,它对用户不是很友好,请设计您的 C# 端代码以使客户端更容易使用您的类,而不是相反。您通常需要一个委托给基 C# 类实现的单行重写方法。

请注意,您的方法签名和 [PreserveSig] 属性的使用存在问题。它们不能通过 IDispatch 调用,也不能自动封送。这需要一个以 HRESULT 作为返回值类型的方法。当你删除属性时它是自动的。

【讨论】:

  • 我最初设置 [PreserveSig] 是为了便于在 VBScript 中进行测试(谁想弄乱 out 参数),但如果没有(并且具有正确的 IDL 定义),它的工作原理相同。我将复制基类中的方法以防止客户端调用 QueryInterface。
  • 恕我直言,这 is 是一个 .net 问题,COM 完全支持 interface(不是类)继承,并且 regasm 可以已生成具有从 ComVisible IBaseClass 派生的 IClass 的 TLB。我注意到发生这种情况时,Class 的 IDispatch 实现也不知道 IBaseClass 方法 - 非常令人失望。在一个 coclass 实现多个接口的情况下,您的建议 (QI) 很好;根据 Jonathan 链接中的 MVP,该技术不适用于继承 层次结构,因为它根本不受支持。
  • @bacar:聚合可以说是更好的术语。无论如何,实际实现接口需要一个具体的类。为了使继承的 COM 接口工作,编译器需要生成多个 v-table,每个接口一个。这仅由支持多重继承的运行时环境支持。 .NET 不是其中之一,这是一个非常基本的限制。是的,您可以称其为 .NET 问题,它不会给您带来任何好处。
  • @Hans:肯定只需要1个(深)vtable,对应最衍生的接口?只有在存在多重继承时才需要多个 vtable,这不是 OP 所追求的。 (即便如此,.net 仍然支持来自多个接口的继承/聚合)。
  • @bacar - 深 v-table 很麻烦。它需要做多个不兼容的职责,支持接口方法指针以及类本身的虚方法。您无法控制指针的位置。 .NET 类总是有虚方法,它继承 System.Object。我不反对 CLR 可以更好地合成一组单独的 v 表,尤其是对于 COM 客户端。但这并没有发生,他们针对的是自动化。入侵 IUnknown 接口是可能的,只是不漂亮。无论如何,没有类型库没什么好说的。
【解决方案2】:

这似乎与这个问题非常相似:Interface inheritance in ComVisible classes in C#

正如您在编辑中所指出的,这是 .net 到 COM 跃点的限制,即 COM 接口不包括任何继承的接口。

在某些语言中,您可以通过在所有相关接口中包含一段代码来解决此问题,但据我所知,这在 C# 中是不可能的。

【讨论】:

  • 事实证明,这将是一些烦人的额外工作,以使我们的程序集可见。
  • 使用 C# 8.0 特征编程,现在可以在接口中包含代码块!
【解决方案3】:

这是一种不同的方法,可以最大限度地减少工作,并保持界面整洁。
只需提供一个返回 BaseInterface 的附加属性:

public IBaseClass BaseProperties {
    get { return this; }
}

当然你必须在你的 IBaseClass 接口中添加这个部分:

public ISingleShot BaseProperties { get; }



下面是对应的VB.Net代码:

ReadOnly Property BaseProperties As ISingleShot
Public ReadOnly Property BaseProperties As IBaseClass Implements IClass.BaseProperties
  Get
    Return Me
  End Get
End Property

【讨论】:

    【解决方案4】:

    使用 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 作为扩展其他接口的属性。 这是一种解决方法。

    【讨论】:

      猜你喜欢
      • 2017-07-23
      • 1970-01-01
      • 2016-12-21
      • 2020-09-09
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      • 2013-09-09
      • 2011-08-12
      相关资源
      最近更新 更多