Uso del enlace anticipado y el enlace en tiempo de ejecución en Automation

Resumen

La forma en que se enlaza a un servidor de Automation puede afectar a muchas cosas del programa, como el rendimiento, la flexibilidad y el mantenimiento.

En este artículo se explican los tipos de enlace disponibles para los clientes de Automation y se ponderan ambos lados de cada método.

Más información

La automatización es un proceso en el que un componente de software se comunica con y/o controla otro componente de software mediante el modelo de objetos de componente (COM) de Microsoft. Es la base para la mayoría de la comunicación entre componentes que se usa en lenguajes como Visual Basic o Visual Basic para Aplicaciones, y se ha convertido en una parte normal de la mayoría de los programas.

Históricamente, un objeto automation es cualquier objeto que admite la interfaz IDispatch. Esta interfaz permite a los clientes llamar a métodos y propiedades en tiempo de ejecución sin tener que conocer el objeto exacto con el que se comunican en tiempo de diseño; un proceso denominado enlace en tiempo de ejecución. Sin embargo, en la actualidad, el término objeto Automation se puede aplicar a prácticamente cualquier objeto COM, incluso a aquellos que no admiten IDispatch (y, por lo tanto, no se pueden enlazar en tiempo de ejecución). En este artículo se supone que el objeto que va a automatizar admite ambos métodos de enlace.

¿Qué es el enlace?

El enlace es un proceso de coincidencia de llamadas de función escritas por el programador al código real (interno o externo) que implementa la función. Se realiza cuando se compila la aplicación y todas las funciones a las que se llama en el código deben enlazarse antes de que se pueda ejecutar el código.

Para comprender el proceso, piense en "enlace" en términos de publicación de un libro. Imagine que el código es como el texto del libro donde en un párrafo determinado ha escrito algo parecido a "vea el capítulo 12, página x para obtener más detalles". No sabe cuál es el número de página hasta que finalice el libro, por lo que antes de que el párrafo se pueda leer según lo previsto, todas las páginas del libro deben enlazarse juntas y el número de página correcto insertado en el párrafo. Esperas a que el libro esté "enlazado" antes de poder hacer referencia a otras partes del libro.

El software de enlace es similar. El código se compone de partes que deben extraerse juntas antes de que el código se pueda "leer". El enlace es el acto de reemplazar los nombres de función por direcciones de memoria (o desplazamientos de memoria, para ser más precisos) donde el código "saltará" cuando se llame a la función. En el caso de los objetos COM, la dirección es un desplazamiento de memoria en una tabla de punteros (denominada tabla v) que contiene el objeto . Cuando se enlaza una función COM, se enlaza a través de la tabla v.

La estructura de un objeto COM es sencilla. Cuando el código contiene una referencia a un objeto, contiene un puntero indirecto a la parte superior de la tabla v. La tabla v es una matriz de direcciones de memoria donde cada entrada es una función diferente a la que se puede llamar en ese objeto. Para llamar a la tercera función en un objeto COM, se saltan tres entradas de la tabla y, a continuación, se saltan a la ubicación de memoria dada allí. Esto ejecuta el código de la función y, cuando se completa, devuelve listo para ejecutar la siguiente línea de código.

+-[Code]------------+  +.................................[COM Object]...+
|                   |  : +-------------+                                :
|Set obj = Nothing -|--->| obj pointer |                                :
|                   |  : +-|-----------+                                :
+-------------------+  :   |   +-----------------+                      :
                       :   +-->| v-table pointer |                      :
                       :       +--|--------------+                      :
                       :          |                                     :
                       :          |  +----------------------------+     :
                       :  (3rd)   |  | Function 1 Address pointer |     :
                       : (Offset) |  +----------------------------+     :
                       :          |  | Function 2 Address pointer |     :
                       :          |  +----------------------------+     :
                       :          +->| Function 3 Address pointer |     :
                       :             +----------------------------+     :
                       +................................................+

En el ejemplo anterior se muestra lo que sucede al liberar un objeto COM. Dado que todos los objetos COM heredan de IUnknown, las tres primeras entradas de la tabla son los métodos de IUnknown. Cuando necesite liberar un objeto, el código llama a la tercera función de la tabla v (IUnknown::Release).

Afortunadamente, Visual Basic realiza este trabajo en segundo plano. Como programador de Visual Basic, nunca tiene que tratar directamente con una tabla v. Pero esta estructura es la forma en que se enlazan todos los objetos COM y es importante que esté familiarizado con ella para comprender qué es el enlace.

Enlace anticipado

El ejemplo anterior es lo que se conoce como enlace temprano (o v-table). Para todos los objetos COM, esta forma de enlace tiene lugar cada vez que se llama a la interfaz IUnknown de un objeto COM. ¿Pero qué hay de las otras funciones del objeto? ¿Cómo se llama a su método Refresh o a su propiedad Parent? Se trata de funciones personalizadas que suelen ser únicas para un objeto . Si no se pueden asumir sus ubicaciones en la tabla v, ¿cómo se encuentran las direcciones de función necesarias para llamarlas?

La respuesta, por supuesto, depende de si sabe o no de antemano cuál es el aspecto de la tabla virtual del objeto. Si lo hace, puede realizar el mismo proceso de enlace anticipado a los métodos personalizados del objeto que lo hizo con sus métodos IUnknown. Esto es lo que generalmente se entiende por "enlace anticipado".

Para usar el enlace anticipado en un objeto, debe saber cuál es su aspecto de la tabla v. En Visual Basic, puede hacerlo agregando una referencia a una biblioteca de tipos que describe el objeto, su interfaz (v-table) y todas las funciones a las que se puede llamar en el objeto. Una vez hecho esto, puede declarar un objeto como un tipo determinado y, a continuación, establecer y usar ese objeto mediante la tabla v. Por ejemplo, si quisiera automatizar Microsoft Office Excel mediante el enlace anticipado, agregaría una referencia a la "Biblioteca de objetos de Microsoft Excel 8.0" desde project| Cuadro de diálogo Referencias y, a continuación, declare la variable como del tipo "Excel.Application". A partir de entonces, todas las llamadas realizadas a la variable de objeto se enlazarían en tiempo de ejecución:


' 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

Este método funciona muy bien la mayor parte del tiempo, pero ¿qué ocurre si no conoce el objeto exacto que va a usar en tiempo de diseño? Por ejemplo, ¿qué ocurre si necesita hablar con varias versiones de Excel o, posiblemente, con un objeto "desconocido"?

Enlace en tiempo de ejecución

COM incluye IDispatch. Se dice que los objetos que implementan IDispatch tienen una dispinterface (si es la única interfaz que admiten) o una interfaz dual (si también tienen una interfaz personalizada a la que se puede enlazar pronto). Se dice que los clientes que se enlazan a IDispatch están "enlazados en tiempo de ejecución" porque la propiedad o el método exactos a los que llaman se determinan en tiempo de ejecución mediante los métodos de IDispatch para localizarlos. Volviendo al ejemplo de libro anterior, piense que es como una nota al pie que le dirige a la tabla de contenido donde tiene que "buscar" el número de página en "tiempo de lectura" en lugar de que ya esté impreso en el texto.

La magia de la interfaz se controla mediante dos funciones: GetIDsOfNames e Invoke. La primera asigna los nombres de función (cadenas) a un identificador (denominado dispid) que representa la función. Una vez que conozca el identificador de la función a la que desea llamar, puede llamarla mediante la función Invoke. Esta forma de invocación de método se denomina "enlace en tiempo de ejecución".

De nuevo, en Visual Basic, la forma en que se especifica cómo se enlaza el objeto es mediante la declaración de objeto. Si declara una variable de objeto como "Object", de hecho, indica a Visual Basic que use IDispatch y, por tanto, es un enlace en tiempo de ejecución:

' 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

Como puede ver, el resto del código es el mismo. La única diferencia entre el enlace anticipado y el enlace en tiempo de ejecución (en términos del código que escribe) está en la declaración de variable.

Es importante tener en cuenta que lo que está "enlazado en tiempo de ejecución" es la función a la que se llama y no la forma en que se llama. En la explicación anterior sobre el enlace en general, debe observar que IDispatch en sí es "límite anticipado", es decir, que Visual Basic realiza la llamada para establecer la propiedad Visible a través de una entrada de tabla v (IDispatch::Invoke) como lo haría con cualquier llamada COM. El propio objeto COM es responsable de reenviar la llamada a la función correcta para que Excel sea visible. Este direccionamiento indirecto permite compilar el cliente de Visual Basic (es decir, enlazado a una dirección de función válida), pero aún no conoce la función exacta que realmente realizará el trabajo.

Enlace desaprobado

Algunos clientes de Automation (principalmente MFC y Visual Basic 3.0, pero también Visual Basic 5.0 y 6.0 con respecto a los controles ActiveX) usan una forma híbrida de enlace en tiempo de ejecución denominada enlace dispid. Si el objeto COM se conoce en tiempo de diseño, los dispids para las funciones a las que se llama se pueden almacenar en caché y pasar directamente a IDispatch::Invoke sin necesidad de llamar a GetIDsOfNames en tiempo de ejecución. Esto puede aumentar considerablemente el rendimiento, ya que en lugar de realizar dos llamadas COM por función, solo necesita realizar una.

El enlace dispid no es una opción que pueda elegir normalmente en Visual Basic 5.0 o 6.0. Se usa para objetos a los que se hace referencia en una biblioteca de tipos pero que no contienen una interfaz personalizada (es decir, para objetos que solo tienen una dispinterface) y para controles ActiveX agregados, pero, en general, Visual Basic usa el enlace anticipado en cualquier lugar que normalmente usaría el enlace dispid.

¿Qué forma de enlace debo usar?

La respuesta a esta pregunta depende tanto del diseño del proyecto como de cualquier otra cosa. Microsoft recomienda el enlace anticipado en casi todos los casos. Sin embargo, puede haber razones para elegir el enlace en tiempo de ejecución.

El enlace anticipado es el método preferido. Es el mejor rendimiento porque la aplicación se enlaza directamente a la dirección de la función a la que se llama y no hay ninguna sobrecarga adicional al realizar una búsqueda en tiempo de ejecución. En términos de velocidad de ejecución general, es al menos el doble de rápido que el enlace en tiempo de ejecución.

El enlace anticipado también proporciona seguridad de tipos. Cuando tiene una referencia establecida en la biblioteca de tipos del componente, Visual Basic proporciona compatibilidad con IntelliSense para ayudarle a codificar cada función correctamente. Visual Basic también le advierte si el tipo de datos de un parámetro o valor devuelto es incorrecto, lo que ahorra mucho tiempo al escribir y depurar código.

El enlace en tiempo de ejecución sigue siendo útil en situaciones en las que la interfaz exacta de un objeto no se conoce en tiempo de diseño. Si la aplicación intenta comunicarse con varios servidores desconocidos o necesita invocar funciones por nombre (por ejemplo, mediante la función CallByName de Visual Basic 6.0), debe usar el enlace en tiempo de ejecución. El enlace en tiempo de ejecución también es útil para solucionar problemas de compatibilidad entre varias versiones de un componente que ha modificado o adaptado incorrectamente su interfaz entre versiones.

Las ventajas dadas al enlace anticipado hacen que sea la mejor opción siempre que sea posible.

Mantener la compatibilidad entre varias versiones

Si va a usar un componente que no redistribuye con el paquete de instalación y no puede estar seguro de la versión exacta con la que se va a comunicar en tiempo de ejecución, debe prestar especial atención al enlace anticipado a una interfaz compatible con todas las versiones del componente, o (en algunos casos) usar el enlace en tiempo de ejecución para llamar a un método que puede existir en una versión determinada y producir un error correctamente si ese método no está presente en la versión instalada en el sistema cliente.

Las aplicaciones de Microsoft Office proporcionan un buen ejemplo de estos servidores COM. Las aplicaciones de Office normalmente expandirán sus interfaces para agregar nuevas funciones o corregir las deficiencias anteriores entre las versiones. Si necesita automatizar una aplicación de Office, se recomienda enlazar a la versión más temprana del producto que espera que se pueda instalar en el sistema del cliente. Por ejemplo, si necesita poder automatizar Excel 95, Excel 97, Excel 2000 y Excel 2002, debe usar la biblioteca de tipos para Excel 95 (XL5en32.olb) para mantener la compatibilidad con las tres versiones.

Las aplicaciones de Office también demuestran que los modelos de objetos con interfaces duales grandes pueden sufrir limitaciones en la serialización en algunas plataformas. Para que el código funcione mejor en todas las plataformas, use IDispatch.

Referencias

Para obtener más información sobre COM, las tablas virtuales y el uso de Automation, consulte los siguientes libros:

Rogerson, Dale, Inside COM, MSPRESS, ISBN: 1-57231-349-8.

Curland, Matt, Advanced Visual Basic 6, DevelopMentor, 0201707128.