2017-04-13 71 views
2

我有以下类:C#COM可见类型:是否需要GUID?

[ComVisible(true)] 
[Guid("F8351C66-7F0E-4E38-AE64-9A262202E230")] 
[ProgId("CarProject.CarFactory")] 
[ClassInterface(ClassInterfaceType.AutoDual)] 
public class CarFactory 
{ 
    public CarFactory() 
    {} 

    [DispId(0)] 
    public Car CreateCar(int tyres) 
    { 
     return new Car(tyres); 
    } 
} 

[ComVisible(true)] 
[Guid("83f622b9-74f4-4700-9167-52c4ce9e79aa")] 
[ClassInterface(ClassInterfaceType.AutoDual)] 
public class Car 
{ 
    [DispId(0)] 
    public int NumberOfTyres { get; private set; } 

    public Car(int tyres) 
    { 
     this.NumberOfTyres = tyres; 
    } 
} 

轿厢目的通过一种工厂创建因此COM客户端仅使用_Car接口自动生成。请注意,没有默认构造函数,因此无法通过COM实例化该类。

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

+0

您的工厂看起来像什么? – stakx

+0

我不认为这是相关的,但我已经更新了与工厂 – peval27

回答

4

不,不需要,因为它永远不会被使用。事实上,这是从来没有需要和应用[Guid]是一个非常糟糕的做法。

这种声明风格还有其他一些问题,讨论它们都需要我写一本书,这不是很实际。您可以更轻松地查看反编译的类型库,以便查看客户端编译器看到的内容。如果您还没有Tlbexp.exe,请使用Visual Studio Developer命令提示符生成类型库。运行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找出注册CLSID for Car不是必需的,这就是为什么你无法在注册表中找回它的原因。

并注意_Object接口。因为每个.NET类都从System.Object派生出现。而且,_Car接口还具有Object(ToString,Equals,GetHashCode,GetType)的方法,因为它们继承了它们。这是你最终依赖于mscorlib.tlb的依据。他们可能可能对客户端程序员有用,但这并不常见,你通常不希望必须记录它们。你揭露的越少越好。


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

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

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

+0

问题感谢您的答案。你总是在这个话题上发现。我们并不担心打破C++兼容性,因为这不太可能从C++中使用此COM。脚本似乎是更好的候选人。相反,我没有考虑到你对mscorlib的引用所说的话! – peval27