关于COM对象的早期绑定和后期绑定

Eddy 发布于2010-9-2 14:8:34 分类: 程序设计 已浏览loading 网友评论0条 我要评论

利用后期绑定可以解决应用程序中使用office组件时不同版本间的兼容性问题。以下内容来自MSDN:

什么是绑定?
绑定是一个将程序员编写的函数调用与实现该函数的实际代码(内部或外部代码)进行匹配的过程。这个过程是在编译应用程序时完成的,而且在可以执行代码之前必须对代码中调用的所有函数进行绑定。

为了理解这一过程,可以将“绑定”看作是出版一本书。请将您的代码想像成书中的文字,在其中的某个段落中,您写下了“有关更多详细信息,请参见第 12 章的第 x 页”这样的句子。在书稿完成之前,您不知道具体的页号,因此,可以在按照设想阅读段落之前,书中的所有页都必须装订在一起并且在该段落中插入正确的页号。在可以引用书中其他部分之前,需要等待书“装订”完毕。

绑定软件的过程与此类似。代码由需要在可以“读取”代码之前组合到一起的各个部分组成。绑定操作是使用内存地址(或更精确的内存偏移量)替换函数名称的过程,在调用函数时代码将“跳转”到这些地址。对于 COM 对象,该地址是由对象保存的指针表(称作向量表)中的一个内存偏移量。在绑定 COM 函数时,将通过向量表来绑定它。

COM 对象的结构十分简单。如果代码持有对某个对象的引用,它便持有了一个指向向量表顶部的间接指针。向量表是一个内存地址数组,其中的每一个条目都对应于可在该对象上调用的一个不同的函数。若要在 COM 对象上调用第三个函数,可以在表中向下跳过三个条目,然后跳到该处给出的内存位置。接下来将执行函数的代码,在执行完毕后会返回并准备执行下一行代码。

+-[代码]------------+ +.................................[COM 对象]...+
| | : +-------------+ :
|Set obj = Nothing -|--->| obj 指针 | :
| | : +-|-----------+ :
+-------------------+ : | +-----------------+ :
: +-->| 向量表指针 | :
: +--|--------------+ :
: | :
: | +----------------------------+ :
: (第三项) | | 函数 1 地址指针 | :
: (偏移量) | +----------------------------+ :
: | | 函数 2 地址指针 | :
: | +----------------------------+ :
: +->| 函数 3 地址指针 | :
: +----------------------------+ :
+................................................+

 

上面的示例显示了在释放 COM 对象时发生的操作。因为所有 COM 对象都继承自 IUnknown,因此表中的前三个条目是 IUnknown 的方法。在需要释放对象时,代码将调用向量表中的第三个函数 (IUnknown::Release)。

幸运的是,此工作由 Visual Basic 在幕后完成。作为 Visual Basic 程序员,您永远不需要直接与向量表打交道。但是,此结构是所有 COM 对象的绑定方式,熟悉它对理解绑定的概念十分重要。
早期绑定
上面的示例介绍了早期(或者向量表)绑定的概念。对于所有 COM 对象,只要调用了 COM 对象的 IUnknown 接口,便会发生这种形式的绑定。但是对于对象的其他函数会怎样呢?如何调用它的 Refresh 方法或其 Parent 属性?这些属于自定义函数,通常是某个对象所特有的。如果无法假定它们在向量表中的位置,如何找出调用它们所需的函数地址?

当然,答案取决于您是否预先知道对象的向量表的形式。如果您知道,那么可以像其 IUnknown 方法一样,对对象的自定义方法执行同样的早期绑定过程。这是“早期绑定”的通常含义。

若要对某个对象使用早期绑定,需要知道其向量表的形式。在 Visual Basic 中,可以通过添加一个对类型库(描述对象、对象的接口(向量表)以及对象中可调用的所有函数)的引用来实现此目的。然后,可以将对象声明为某种特定类型,然后使用向量表设置和使用该对象。例如,如果想要使用早期绑定来自动化 Microsoft Office Excel,应该从“项目|引用”对话框中添加对“Microsoft Excel 8.0 对象库”的引用,然后将变量声明为“Excel.Application”类型。此后,对该对象变量的所有调用都将是早期绑定的:
' Set reference to 'Microsoft Excel 8.0 Object Library' in
' the Project|References dialog (or Tools|References for VB4 or VBA).

' Declare the object as an early-bound object
Dim oExcel As Excel.Application

Set oExcel = CreateObject("Excel.Application")

' The Visible property is called via the v-table
oExcel.Visible = True
 

这种方法在大多数时间都工作良好,但是如果您在设计时不知道将要使用的具体对象应该怎么办?例如,如果您需要操作多个版本的 Excel,或者可能需要操作某个完全“未知”的对象,这时应如何处理?
后期绑定
COM 包括 IDispatch。实现 IDispatch 的对象可以说具有调度接口(如果该接口是它们支持的唯一接口)或双接口(如果它们还具有可早期绑定的自定义接口)。绑定到 IDispatch 的客户端可以说是“后期绑定的”,因为它们调用的具体属性或方法是在运行时使用 IDispatch 的方法来定位和确定的。让我们回到前面的那个出版书的例子,将它想像为可将您引导到目录的一个脚注,您可以在“阅读时”在目录中查找页号,而不是将它预先印刷在文本中。

可通过两个函数控制接口的神奇力量:GetIDsOfNames 和 Invoke。第一个函数将函数名(字符串)映射到表示该函数的一个标识符(称作 dispid)。在知道了要调用的函数的 ID 之后,可以使用 Invoke 函数调用它。这种形式的方法调用称作“后期绑定”。

在 Visual Basic 中,仍旧是通过对象声明来指定对象的绑定方式。如果将对象变量声明为“Object”,实际上,您是在告诉 Visual Basic 使用 IDispatch,因此属于后期绑定:
' No reference to a type library is needed to use late binding.
' As long as the object supports IDispatch, the method can
' be dynamically located and invoked at run-time.

' Declare the object as a late-bound object
Dim oExcel As Object

Set oExcel = CreateObject("Excel.Application")

' The Visible property is called via IDispatch
oExcel.Visible = True
 

如同您看到的,代码的其余部分是相同的。早期绑定和后期绑定之间的唯一差别(在编写的代码方面)是变量的声明方式。

进行“后期绑定”的是要调用的函数而不是函数的调用方式,注意到这一点十分重要。通过前面对绑定的大致讨论,您应该注意到 IDispatch 本身属于“早期绑定”:也就是说,Visual Basic 通过向量表条目 (IDispatch::Invoke) 来进行调用以设置 Visible 属性,就如同对任何 COM 调用一样。COM 对象本身负责将调用转发给正确的函数以便使 Excel 可见。这种间接性使得 Visual Basic 客户端在编译时(即在绑定到某个有效函数地址时)无需知道实际执行工作的具体函数。
Dispid 绑定
一些自动化客户端(最明显的是 MFC 和 Visual Basic 3.0,还有 Visual Basic 5.0 和 6.0 的 ActiveX 控件)使用后期绑定的一种混合形式(称作 dispid 绑定)。如果 COM 对象在设计时是已知的,所调用函数的 dispid 可以进行缓存并直接传递给 IDispatch::Invoke,而无需在运行时调用 GetIDsOfNames。这样可极大地提高程序的性能,因为不必对每个函数进行两次 COM 调用,只需调用一次即可。

正常情况下,在 Visual Basic 5.0 或 6.0 中不应选择使用 Dispid 绑定。它可用于在类型库中进行了引用但是不包含自定义接口的对象(即,用于只具有调度接口的对象),并且可用于聚合的 ActiveX 控件,但是通常情况下,Visual Basic 会在本可以正常使用 dispid 绑定的任何地方使用早期绑定。


我应使用哪一种绑定形式?
与其他问题一样,这个问题的答案很大程度上取决于项目的设计。Microsoft 建议在几乎所有情况下都使用早期绑定。但是,也有一些选择使用后期绑定的理由。

早期绑定是首选方法。它具有最佳的性能,因为应用程序直接绑定到待调用函数的地址,并且不会因执行运行时查找而产生任何额外的开销。在整体执行速度方面,它至少比后期绑定快两倍。

此外,早期绑定还可提供类型安全性。如果引用了组件的类型库,Visual Basic 会提供 IntelliSense 支持,帮助您正确编码每个函数。如果参数或返回值的数据类型有误,Visual Basic 还会发出警告,在编写和调试代码时可节省大量时间。

对于在设计时不知道对象的具体接口的情况,后期绑定依然十分有用。如果应用程序需要操作多个未知服务器或者需要按名称调用函数(例如,使用 Visual Basic 6.0 的 CallByName 函数),那么便需要使用后期绑定。此外,对于组件的多个版本不正确地修改或改编其接口的情况,后期绑定还有助于解决多个版本的兼容性问题。

由于早期绑定具有许多优点,只要可能,早期绑定都是您的最佳选择。
维护多个版本间的兼容性
如果您要使用的某个组件不会随同安装程序包一起再次分发,并且无法肯定将在运行时与之进行通信的确切版本,应该特别注意接口的早期绑定是否兼容组件的所有版本,或者(在某些情况下)使用后期绑定调用可能存在于某个特定版本中的方法,并且当该方法在客户端系统所安装的版本中不存在时对出现的错误妥善进行处理。

Microsoft Office 应用程序提供了上述 COM 服务器的一个很好的例子。通常,Office 应用程序会扩展其接口,添加新功能或纠正先前版本的缺点。如果需要自动化某个 Office 应用程序,建议早期绑定到预计客户端系统中可能安装的最早的产品版本。例如,如果需要自动化 Excel 95、Excel 97、Excel 2000 和 Excel 2002,应使用 Excel 95 的类型库 (XL5en32.olb) 以保持与所有三个版本的兼容性。

此外,Office 应用程序还演示了具有大型双接口的对象模型会受到在某些平台上封送数据方面的限制。为了使代码在所有平台上都能很好地工作,请使用 IDispatch。

已经有(0)位网友发表了评论,你也评一评吧!
原创文章如转载,请注明:转载自Eddy Blog
原文地址:http://www.rrgod.com/program/555.html     欢迎订阅Eddy Blog

关于 COM对象  早期绑定  后期绑定  的相关文章

记住我的信息,下次不用再输入 欢迎给Eddy Blog留言