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

C# C COM可见类型:是否需要GUID?,c#,com,C#,Com,我有以下课程: [ComVisible(true)] [Guid("F8351C66-7F0E-4E38-AE64-9A262202E230")] [ProgId("CarProject.CarFactory")] [ClassInterface(ClassInterfaceType.AutoDual)] public class CarFactory { public CarFactory() {} [DispId(0)] public Car CreateC

我有以下课程:

[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;
    }
}
Car对象由工厂创建,因此COM客户端仅使用自动生成的_Car接口。请注意,没有默认构造函数,因此无法通过COM实例化该类


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

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

这种声明风格还有其他几个问题,讨论这些问题都需要我写一本书,这不太实际。对您来说,查看反编译类型库会容易得多,这样您就可以看到客户机编译器所看到的内容。使用Visual Studio Developer命令提示符,如果尚未使用Tlbexp.exe生成类型库,则生成类型库。运行Oleview.exe,File>View 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中的Object browser)隐藏声明。使用OleView查看详细信息非常重要。除此之外,一个怪癖可以追溯到那些不支持接口的语言,老版本的VisualBasic(如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,这就是为什么您无法在注册表中找到它

请注意_对象接口。出现的原因是每个.NET类都派生自System.Object。同样地,_Car接口也有Object-ToString、Equals、GetHashCode和GetType方法,因为Car继承了它们。这就是您最终依赖mscorlib.tlb的原因。它们可能对客户端程序员有用,但这并不常见,您通常不想记录它们。你暴露的越少越好

长话短说,发生这种情况是因为您使用了ClassInterfaceType.AutoDual。这是一种方便,但不是一种很好的方式,Microsoft强烈反对,建议改为使用ClassInterfaceType.AutoDispatch。但这只对脚本语言有用

通过显式声明ICar接口而不是让类型库导出器为您生成它,可以避免所有这些。它需要是[ComVisibletrue],并且类需要实现它。现在您可以使用ClassInterfaceType.None


现在您可以给接口一个[Guid]。但是要注意,COM要求接口是不可变的,这是一种不好的做法。一旦你把它暴露在野外,你就再也不能改变它了。非常重要的是,COM有一个令人讨厌的DLL地狱问题,这是由注册在机器范围内引起的,而您通常无法保证能够重新编译客户端程序。这会导致很难诊断的故障。如果您必须更改它,那么接口需要一个新的[Guid],以便旧接口和新接口可以共存。最简单的方法是完全忽略属性,CLR已经自动生成了它。使用[Guid]确实允许创建与旧接口声明二进制兼容的接口,但这很难正确执行。

我认为这与此无关,但我已经用工厂更新了问题谢谢您的回答。在这个话题上,你总是一针见血。我们不太担心打破C++兼容性,因为这个COM不太可能从C++中使用。脚本似乎是更好的选择。相反,我没有考虑你所说的关于mscorlib的引用!