导入类型转换

本主题描述导入过程如何转换以下类型:

  • 接口

  • 结构

  • 枚举

  • 常量

  • Typedef

总的说来,Tlbimp.exe 将以类型在原类型库的相同名称来导入类型。 类型库中的名称必须是唯一的,以避免在转换过程中发生命名冲突。 所有有效的类型库名称都是有效的程序集名称。

导入的类型通过它们所属的命名空间来确定范围,命名空间与原类型库相同。 类型将分别用完整的命名空间和类型名称来进行标识。

通过使用类型库中的类型库特性,可以显式地控制导入类型的托管名称。 此用户定义的特性标识符为 0F21F359-AB84-41e8-9A78-36D110E6D2F9。 以下类型库将显示如何添加此用户定义的特性。

类型库表示形式

[  uuid(…),
    version(1.0)
]
library AcmeLib {
    interface Widget {};
    [custom(0F21F359-AB84-41e8-9A78-36D110E6D2F9, 
     "Acme.WidgetLib.Slingshot")]
    coclass Slingshot {};
};

即使 Tlbimp.exe 将类型库导入 AcmeLib 命名空间,Slingshot 类仍会成为 Acme.WidgetLib.Slingshot。

接口

当导入过程转换接口时,它将去除所有的 IUnknownIDispatch 方法。 此转换将 GuidAttribute 应用于接口,以保留在类型库中分配的接口标识符 (IID) 以及 InterfaceTypeAttribute,除非该接口是双重接口(从 IDispatch 派生的接口)。

类型库表示形式

[uuid(…), ]
interface IWidget : IUnknown {
    HRESULT New()
    HRESULT Start()
};
[uuid(…), ]
interface IGadget : IWidget {
    HRESULT Baz()
};

在转换过程中,导入过程会将基接口的方法添加到导出接口中。 下面的示例将 New 和 Start 添加到 IGadget 接口中:

<Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Interface IWidget
    Sub [New]()
    Sub Start()
End Interface

<Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Interface IGadget
    Inherits IWidget
    Shadows Sub [New]()
    Shadows Sub Start()
    Sub Baz()
End Interface
[Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IWidget {
    void New();
    void Start();
};
[Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IGadget : IWidget {
    new void New();
    new void Start();
    void Baz();
};

导入进程将创建一个托管类来表示每个 COM coclass,并为该托管类提供与初始 coclass 相同的名称并加上 Class。 例如,NewNewer coclass 会成为 NewNewerClass。 此转换会将 GuidAttribute 添加到类中,以捕获 coclass 的类标识符 (CLSID)。

除托管类之外,导入进程还会添加与 coclass 同名的接口,并应用 CoClassAttribute 以标识初始 coclass 的 CLSID。 该接口与 coclass 的默认接口具有相同的 IID。 通过该接口,客户端可以始终作为事件接收器进行注册。

与 COM coclass 不同,托管类可以包含类成员。 为了与 .NET Framework 的处理方法保持一致,此转换将在每个类中添加与 coclass 所实现的每个接口相关联的成员。 托管类的用户可以调用托管类型的方法和属性,而无须首先转换到特定的接口。 导入过程还会将一个默认构造函数添加到每个经过转换的 coclass 中。 利用构造函数,可以从托管代码创建类。 (不能创建无构造函数的类)。 默认的构造函数没有参数;其实现将调用基类构造函数。 如果将 noncreatable 类型库特性应用于 coclass,导入过程就不会为类创建默认构造函数。

由于接口成员名称并非总是唯一的,成员之间可能会出现名称和 DispId 的冲突。 为了解决名称冲突,TlbImp.exe 将在类的每个冲突成员的名称前带上接口名加下划线形式的前缀。 只要成员的名称发生冲突,在 coclass 语句中列出的第一个接口就将保持不变。

当发生 DispId 冲突时,导入过程将为 coclass 默认接口的成员分配 DispId,而不会为冲突的类成员分配 DispId。 但是,导入过程始终会给接口的成员分配 DispId。

类型库表示形式

[uuid(…)]
interface INew : IDispatch {
    [id(0x100)] HRESULT DoFirst();
    [id(0x101)] HRESULT DoSecond();
}
[uuid(…)]
interface INewer : IDispatch {
    [id(0x100)] HRESULT DoNow();
    [id(0x101)] HRESULT DoSecond();
}
[uuid(…)]
coclass NewNewer  {
    [default] interface INew;
    interface INewer;
}

转换的类型如下所示:

<Guid(…)> Public Interface INew
    …
End Interface

<Guid(…)> Public Interface INewer
    …
End Interface

<Guid(…)> Public Interface NewNewer
Inherits INew
    …
End Interface

<Guid(…)> Public Class NewNewerClass
Implements INew   
Implements INewer
Implements NewNewer
' Method implementation
     <DispId(100)> _
      …
End Class  
[Guid(…)]
public interface INew {…}

[Guid(…)]
public interface INewer {…}

[Guid(…)]
public interface NewNewer : INew {…}

[Guid(…)]
public class NewNewer : INew, INewer, NewNewer{
// Method implementation.
     [DispId(100)]…
}

结构

在类型库中定义的结构将当作元数据导入。 如果某一结构的字段为引用类型,Tlbimp.exe 会将该类型作为 IntPtr 导入并应用 ComConversionLossAttribute。 该特性指示在导入进程中丢失了信息。

枚举

类型库导入程序 (Tlbimp.exe) 将非托管枚举作为托管 Enum 类型导入。

常量

在此版本中,常数将不从类型库中导入。

Typedef

不导入类型库中的类型定义 (Typedef)。 而是将参数和字段当作基础类型导入。 例如,BUTTON_COLOR 类型的参数将当作整数类型导入,这是因为 BUTTON_COLOR 是整数的别名。

导入之后,参数和字段将在 ComAliasNameAttribute 中承载使它们与其原始类型相关联的信息。 此转换过程将应用 ComAliasNameAttribute,使字段、参数或返回值与类型库的名称以及库中用作别名的类型相关联。

以下类型库表示形式将 cl 参数的类型设置为 BUTTON_COLOR,该类型是整数的另一种叫法。

类型库表示形式

library MyLib {
    typedef [public] int BUTTON_COLOR;

    interface ISee {
        HResult SetColor([in] BUTTON_COLOR cl);
        HResult GetColor([out, retval] BUTTON_COLOR *cl);
    };
   
    coclass See {
        [default] interface ISee
    };
};

转换的类型如下所示:

public interface ISee {
    void SetColor([ComAliasName("MyLib.BUTTON_COLOR")]] int cl);
    [return: ComAliasName("MyLib.BUTTON_COLOR")] int GetColor();
};

public class See {
    public void SetColor([ComAliasName("MyLib.BUTTON_COLOR")]] int cl);
    [return: ComAliasName("MyLib.BUTTON_COLOR")] int GetColor();
};

请注意,所得的元数据中没有定义实际的 BUTTON_COLOR 类型。 而是将类型库中定义为 BUTTON_COLOR 类型的参数转换为基础类型 int,并且用 ComAliasNameAttribute 特性化。 此转换过程还将该特性应用于类方法的参数。

请参见

概念

导入库转换

导入模块转换

导入成员转换

导入参数转换

其他资源

有关从类型库转换到程序集的摘要