C#COM可见类型:需要GUID吗?

时间:2017-04-13 09:15:39

标签: c# com

我有以下课程:

_Car

Car对象由工厂创建,因此COM客户端仅使用自动生成的Guid接口。请注意,没有默认构造函数,因此无法通过COM实例化类。

我的问题是:是否需要{{1}}属性?我无法在注册表中找到它的价值。

1 个答案:

答案 0 :(得分:4)

不,它不需要,因为它永远不会被使用。事实上,它永远不需要,并且应用[Guid]是一种非常糟糕的做法。

这种宣言风格还有其他几个问题,讨论它们都需要我写一本书,这不太实用。您可以更轻松地查看反编译类型库,以便查看客户端编译器看到的内容。使用Visual Studio Developer命令提示符,如果还没有Tlbexp.exe,则生成类型库。运行Oleview.exe,文件>查看Typelib并选择.tlb文件。突出显示您现在看到的相关详细信息:

importlib("mscorlib.tlb");

这非常尴尬,客户端程序员不仅需要添加对类型库的引用,而且 mscorlib.dll的.NET类型库。存储在c:\ windows \ microsoft.net \ framework \ v4.0.30319目录中。或者v2.0.50727,旧版本。非常不直观,并不经常达到良好的目的。请注意您在上一个问题中遇到的问题。请继续阅读以了解发生这种情况的原因。

[
  odl,
  uuid(BD7A2C0E-E561-3EBC-8BB7-1C72EE61D5B0),
  hidden,
  dual,
  nonextensible,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "ClassLibrary171.Car")    

]
interface _Car : IDispatch {
    // etc..
}

这是自动生成的“类接口”,您现在已经了解它。这是客户端编译器实际用于进行调用的那个。注意[uuid]属性,与C#中的[Guid]属性相同。但它有一个非常随机的价值​​。它是自动生成的,就像界面一样。因此,在你的声明中使用[Guid]实际上并没有完成任何事情。

[hidden]属性以及接口名称的前导下划线告诉类型浏览器工具(如VS中的对象浏览器)隐藏声明。使用OleView查看详细信息非常重要。否则,回到那种不支持接口的语言的怪癖,旧版本的Visual Basic(如VB6和VBA)和脚本语言(如VBScript和JavaScript)就是主要的例子。这些语言需要通过使界面看起来像一个类来模拟它们,这是你考虑暴露这个类的唯一原因。

[
  uuid(83F622B9-74F4-4700-9167-52C4CE9E79AA),
  version(1.0),
  noncreatable,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "ClassLibrary171.Car")
]
coclass Car {
    [default] interface _Car;
    interface _Object;
};

注意[noncreatable]属性。类型库导出器可以看到客户端代码永远不能创建Car实例,因为它没有默认构造函数。这反过来又帮助Regasm.exe弄清楚是否需要注册Car的CLSID,这就是为什么你无法在注册表中找到它。

并注意_Object界面。出现是因为每个.NET类都派生自System.Object。同样,_Car接口也有Object(ToString,Equals,GetHashCode,GetType)的方法,因为Car继承了它们。这就是你最终对mscorlib.tlb的依赖。它们可能对客户端程序员有用,但这并不常见,您通常不希望记录它们。你曝光的越少越好。

长话短说,这是因为您使用了ClassInterfaceType.AutoDual。这是一种方便,但不是很好,微软强烈反对它,推荐使用ClassInterfaceType.AutoDispatch。但这只对脚本语言有用。

通过明确声明ICar接口而不是让类型库导出器为您生成它来避免这一切。它需要是[ComVisible(true)],并且类需要实现它。现在您可以使用ClassInterfaceType.None

现在你可以给界面一个[Guid]。但要注意这是不好的做法,COM要求接口不可变。一旦你在野外露出一个,你永远不能再改变它。非常重要的是,COM有一个令人讨厌的DLL Hell问题,这是由机器范围内的注册引起的,并且您通常无法保证可以重新编译客户端程序。崩溃可能导致很难诊断。如果你必须更改它,那么界面需要一个新的[Guid],因此旧界面和新界面都可以共存。通过简单地完全省略属性来完成,CLR已经自动生成它。使用[Guid]确实允许创建与旧接口声明二进制兼容的接口,但这很难正确执行。