导入成员转换
更新:2007 年 11 月
本主题描述导入过程如何转换以下成员:
方法
属性
事件
Tlbimp.exe 将 DefaultMemberAttribute 应用于任何 DispID 为 0 的方法或属性。请注意,C# 开发人员必须将这些成员当作数组来处理。有关其他信息,请参见编程语言文档。
方法
当 COM 互操作导入 COM 类型时,它将生成等效于原 COM 方法签名的 .NET Framework 方法签名。在转换过程中,它会将 COM 中的参数、返回值和 HRESULT 映射到 .NET Framework 方法签名中的相应对象,如下图所示。
方法签名转换
您可以通过对象查看器或反射来检查方法语法,就像处理其他任何 .NET 类一样。默认情况下,当 COM 对象返回失败的 HRESULT 时,运行库将引发相应的异常。
属性
COM 开发人员可以在接口上声明属性和方法。所有属性都具有相应的访问器方法,用于设置或获取属性值。当导入过程利用属性将接口的类型库说明转换为元数据时,它将创建一个属性并为属性创建一个或多个访问器方法。
类型库转换过程将按以下方式转换属性访问器方法:
具有 [propget] 属性 (attribute) 的属性 (property) 将成为相同类型的托管属性 (property),它带有名为 get_propertyname 的相应方法。
带有 [propput] 属性 (attribute) 或 [propputref] 属性 (attribute) 的属性 (property) 将成为相同类型的托管属性 (property),它带有名为 set_propertyname 的相应方法。
同时带有 [propput] 和 [propputref] 属性 (attribute) 的属性 (property) 将成为:
与 [propputref] 属性 (attribute) 具有相同类型的托管属性 (property),它带有名为 set_propertyname 的相应方法。
另一个与 [propput] 属性 (attribute) 具有相同类型的访问器方法,它具有名称 let_propertyname。
以下类型库将显示最初的属性。
类型库表示形式
interface ISample : IDispatch {
[propget] HRESULT prop1([out, retval] short *pVal);
[propput] HRESULT prop1([in] short newVal);
[propget] HRESULT prop2([out, retval] INew **pVal);
[propputref] HRESULT prop2([in] INew *newVal);
[propget] HRESULT prop3([out, retval] INew **ppINew);
[propput] HRESULT prop3([in] BSTR text);
[propputref] HRESULT prop3([in] INew *pINew);
}
转换后的属性显示在以下 Visual Basic 2005 代码段中。
Public Property
Get Prop1() As Integer … End Get
Set Prop1(val as Integer) … End Set
End Property
Public Property
Get Prop2() As INew … End Get
Set Prop2(val as INew) … End Set
End Property
Public Property
Get Prop3() As INew … End Get
Set Prop3(val as INew) … End Set
End Property
Public let_prop3(String as Text)
事件
COM 类型库可以定义用于事件的接口。在该库中,指明事件出处的 coclass 可以通过指定 [source] 属性来标识事件接口。事件接收器会实现接口,而事件源则使用接口。未在类型库中描述的 COM 连接点接口会将事件接收器连接到事件源。
在以下 IDL 代码示例中,Button 类实现 IButton 接口,并指明 IButtonEvents 接口上事件的出处。
interface IButton {
HRESULT Init();
}
interface IButtonEvents {
HRESULT Click([in] int x, [in] int y);
HRESULT Resize([out, retval] int *pRetval};
}
coclass Button {
[default] interface IButton;
[default, source] interface IButtonEvents;
}
.NET 事件模型与 COM 连接点模型之间存在很大的差异。接收事件的托管类是通过将委托传递给事件源(而不是使用 COM 连接点)来完成类似任务的。COM 互操作服务消除了这两种不同的事件模型之间的差异。
在导入过程中,Tlbimp.exe 将创建几个类型,使托管应用程序能够接收由使用 .NET 事件模型的非托管类指明其出处的事件。在以下步骤中,Tlbimp.exe 将为上例所示的 Button 类生成类和接口。
导入进程为事件接口中的每一事件创建一个委托类型。委托名称由事件接收接口、下划线、事件名称和 EventHandler 一词组成。例如,在上例的类型库中,Click 事件将变成 IButtonEvents_ClickEventHandler 委托。
' A delegate for each event. Delegate Sub IButtonEvents_ClickEventHandler(ByVal x As Integer, _ ByVal y As Integer) Delegate Function IButtonEvents_ResizeEventHandler() As Integer
// A delegate for each event. delegate void IButtonEvents_ClickEventHandler(int x, int y); delegate int IButtonEvents_ResizeEventHandler();
请注意,委托的签名由非托管方法签名直接转换而来。
Tlbimp.exe 以常规方式导入默认接口,并使接口名称保持不变。在此示例中,接口名为 IButton。
' Direct import of original default interface. Public Interface IButton Sub Init() End Interface
// Direct import of original default interface. public interface IButton { void Init(); }
Tlbimp.exe 以常规方式导入事件接口,并使接口名称保持不变。在此示例中,接口名为 IButtonEvent。
' Direct import of original event interface. ' Not of interest to managed sinks. Public Interface IButtonEvents Sub Click(ByVal x As Integer, ByVal y As Integer) Function Resize() As Integer End Interface
// Direct import of original event interface. // Not of interest to managed sinks. public interface IButtonEvents { void Click(int x, int y); int Resize(); }
Tlbimp.exe 还会创建另一个事件接口,该接口通过在原接口名称上添加“_Event”后缀来标明。第二个事件接口具有 Click 和 Resize 成员事件。它还具有 add 和 remove 方法,用于事件委托。在此示例中,接口名为 IButtonEvents_Event。
' Modified version of the event interface with events ' for managed sinks.
Public Interface IButtonEvents_Event Sub Click As IButtonEvents_Click Function Resize() As IButtonEvents_Resize Sub add_Click(ByVal Click As IButtonEvents_ClickEventHandler) Sub remove_Click(ByVal Click As _ IButtonEvents_ClickEventHandler) Sub add_Resize(ByVal Resize As _ IButtonEvents_ResizeEventHandler) Sub remove_Resize(ByVal Resize As _ IButtonEvents_ResizeEventHandler) End Interface // Modified version of the event interface with events // for managed sinks. public interface IButtonEvents_Event { IButtonEvents_Click Click; IButtonEvents_Resize Resize; void add_Click(IButtonEvents_ClickEventHandler ); void remove_Click(IButtonEvents_ClickEventHandler ); void add_Resize(IButtonEvents_ResizeEventHandler ); void remove_Resize(IButtonEvents_ResizeEventHandler ); }
说明: 在需要强制转换成事件接口的少数情况下,要强制转换成的是 Tlbimp.exe 所生成的接口,而不是原接口。例如,必须强制转换成 IButtonEvents_Event,而不是 IButtonEvents。
Tlbimp.exe 导入指明事件出处的 coclass 以确保包括所有显式实现的接口,并在原类名后加上 Class。例如,Button coclass 会成为 ButtonClass。Tlbimp.exe 还会生成一个名称与该 coclass 相同的 coclass 接口。该接口实现带有 _Event 后缀的事件接口。
' This is the imported coclass interface. ' Note the underscore in IButtonEvents_Event. Public Interface Button Inherits IButton Inherits IButtonEvents_Event End Interface Public Class ButtonClass Implements Button Implements IButton Implements IButtonEvents_Event Sub Init() End Sub 'Init End Class
// This is the imported coclass interface. // Note the underscore in IButtonEvents_Event. public interface Button:IButton, IButtonEvents_Event {} public class ButtonClass:Button,IButton,IButtonEvents_Event { void Init(){} }