Solucionar problemas de interoperabilidad (Visual Basic)

Al interoperar entre COM y el código administrado de .NET Framework, es posible que encuentre uno o varios de los siguientes problemas comunes.

Serialización de interoperabilidad

En ocasiones, es posible que tenga que usar tipos de datos que no forman parte de .NET Framework. Los ensamblados de interoperabilidad controlan la mayor parte del trabajo de los objetos COM, pero es posible que tenga que controlar los tipos de datos que se usan cuando los objetos administrados se exponen a COM. Por ejemplo, las estructuras de las bibliotecas de clases deben especificar el tipo no administrado BStr en las cadenas enviadas a objetos COM creados por Visual Basic 6.0 y versiones anteriores. En tales casos, puede usar el atributo MarshalAsAttribute para que los tipos administrados se expongan como tipos no administrados.

Exportación de cadenas de longitud fija a código no administrado

En Visual Basic 6.0 y versiones anteriores, las cadenas se exportan a objetos COM como secuencias de bytes sin un carácter de terminación NULL. Por compatibilidad con otros lenguajes, Visual Basic .NET incluye un carácter de terminación al exportar cadenas. La mejor manera de solucionar esta incompatibilidad es exportar las cadenas que carecen del carácter de terminación como matrices de Byte o Char.

Exportación de jerarquías de herencia

Las jerarquías de clases administradas se acoplan cuando se exponen como objetos COM. Por ejemplo, si define una clase base con un miembro y, luego, hereda la clase base en una clase derivada que se expone como un objeto COM, los clientes que usan la clase derivada en el objeto COM no podrán usar los miembros heredados. Solo se puede tener acceso a los miembros de clase base desde objetos COM como instancias de una clase base y solo si la clase base también se crea como un objeto COM.

Métodos sobrecargados

Aunque puede crear métodos sobrecargados con Visual Basic, no son compatibles con COM. Cuando una clase que contiene métodos sobrecargados se expone como un objeto COM, se generan nuevos nombres de método para los métodos sobrecargados.

Por ejemplo, considere una clase que tiene dos sobrecargas del método Synch. Cuando la clase se expone como un objeto COM, los nuevos nombres de método generados podrían ser Synch y Synch_2.

El cambio de nombre puede causar dos problemas a los consumidores del objeto COM.

  1. Es posible que los clientes no esperen los nombres de método generados.

  2. Los nombres de método generados en la clase expuesta como un objeto COM pueden cambiar cuando se agregan nuevas sobrecargas a la clase o a su clase base. Como consecuencia, se pueden producir problemas de versiones.

Para resolver ambos problemas, asigne a cada método un nombre único, en lugar de usar la sobrecarga, al desarrollar objetos que se expongan como objetos COM.

Uso de objetos COM mediante ensamblados de interoperabilidad

Los ensamblados de interoperabilidad se usan casi como si fueran reemplazos de código administrado en los objetos COM que representan. Sin embargo, dado que son contenedores y no objetos COM reales, hay algunas diferencias entre el uso de ensamblados de interoperabilidad y ensamblados estándar. Estas áreas de diferencia incluyen la exposición de clases y tipos de datos para parámetros y valores devueltos.

Clases expuestas como interfaces y clases

A diferencia de las clases de ensamblados estándar, las clases COM se exponen en ensamblados de interoperabilidad como una interfaz y una clase que representa la clase COM. El nombre de la interfaz es idéntico al de la clase COM. El nombre de la clase de interoperabilidad es el mismo que el de la clase COM original, pero con la palabra "Class" anexada. Por ejemplo, supongamos que tiene un proyecto con una referencia a un ensamblado de interoperabilidad en un objeto COM. Si la clase COM se denomina MyComClass, IntelliSense y el Explorador de objetos muestran una interfaz denominada MyComClass y una clase denominada MyComClassClass.

Creación de instancias de una clase de .NET Framework

Por lo general, se crea una instancia de una clase de .NET Framework mediante la instrucción New con un nombre de clase. Tener una clase COM representada por un ensamblado de interoperabilidad es el caso en el que se puede usar la instrucción New con una interfaz. A menos que use la clase COM con una instrucción Inherits, puede usar la interfaz igual que haría con una clase. En el código siguiente se muestra cómo crear un objeto Command en un proyecto que tiene una referencia al objeto COM de la biblioteca Microsoft ActiveX Data Objects 2.8:

Dim cmd As New ADODB.Command

Sin embargo, si usa la clase COM como base para una clase derivada, debe usar la clase de interoperabilidad que representa la clase COM, como en el código siguiente:

Class DerivedCommand
    Inherits ADODB.CommandClass
End Class

Nota

Los ensamblados de interoperabilidad implementan implícitamente interfaces que representan clases COM. No debe intentar usar la instrucción Implements para implementar estas interfaces o se producirá un error.

Tipos de datos para parámetros y valores devueltos

A diferencia de los miembros de ensamblados estándar, los miembros de ensamblados de interoperabilidad pueden tener tipos de datos que difieren de los usados en la declaración de objeto original. Aunque los ensamblados de interoperabilidad convierten implícitamente los tipos COM en tipos de Common Language Runtime compatibles, debe prestar atención a los tipos de datos que usan ambas partes para evitar errores en tiempo de ejecución. Por ejemplo, en objetos COM creados en Visual Basic 6.0 y versiones anteriores, los valores de tipo Integer asumen el tipo equivalente de .NET Framework, Short. Se recomienda usar el Explorador de objetos para examinar las características de los miembros importados antes de usarlos.

Métodos COM de nivel de módulo

La mayoría de los objetos COM se usan mediante la creación de una instancia de una clase COM con la palabra clave New y la llamada posterior a métodos del objeto. Una excepción a esta regla implica objetos COM que contienen clases COM AppObj o GlobalMultiUse. Estas clases se asemejan a los métodos de nivel de módulo en las clases de Visual Basic .NET. Visual Basic 6.0 y versiones anteriores crean implícitamente instancias de estos objetos la primera vez que se llama a uno de sus métodos. Por ejemplo, en Visual Basic 6.0 puede agregar una referencia a la biblioteca de objetos Microsoft DAO 3.6 y llamar al método DBEngine sin crear primero una instancia:

Dim db As DAO.Database  
' Open the database.  
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")  
' Use the database object.  

Visual Basic .NET requiere que siempre cree instancias de objetos COM antes de poder usar sus métodos. Para usar estos métodos en Visual Basic, declare una variable de la clase deseada y use la nueva palabra clave para asignar el objeto a la variable de objeto. La palabra clave Shared se puede usar cuando quiera asegurarse de que solo se crea una instancia de la clase.

' Class level variable.
Shared DBEngine As New DAO.DBEngine

Sub DAOOpenRecordset()
    Dim db As DAO.Database
    Dim rst As DAO.Recordset
    Dim fld As DAO.Field
    ' Open the database.
    db = DBEngine.OpenDatabase("C:\nwind.mdb")

    ' Open the Recordset.
    rst = db.OpenRecordset(
        "SELECT * FROM Customers WHERE Region = 'WA'",
        DAO.RecordsetTypeEnum.dbOpenForwardOnly,
        DAO.RecordsetOptionEnum.dbReadOnly)
    ' Print the values for the fields in the debug window.
    For Each fld In rst.Fields
        Debug.WriteLine(fld.Value.ToString & ";")
    Next
    Debug.WriteLine("")
    ' Close the Recordset.
    rst.Close()
End Sub

Errores no controladas en controladores de eventos

Un problema común de interoperabilidad son los errores en los controladores de eventos que controlan los eventos generados por objetos COM. Estos errores se omiten a menos que compruebe específicamente si hay errores mediante instrucciones On Error o Try...Catch...Finally. Por ejemplo, el ejemplo siguiente procede de un proyecto de Visual Basic .NET que tiene una referencia al objeto COM de la biblioteca Objetos de datos ActiveX de Microsoft 2.8.

' To use this example, add a reference to the 
'     Microsoft ActiveX Data Objects 2.8 Library  
' from the COM tab of the project references page.
Dim WithEvents cn As New ADODB.Connection
Sub ADODBConnect()
    cn.ConnectionString =
    "Provider=Microsoft.Jet.OLEDB.4.0;" &
    "Data Source=C:\NWIND.MDB"
    cn.Open()
    MsgBox(cn.ConnectionString)
End Sub

Private Sub Form1_Load(ByVal sender As System.Object,
    ByVal e As System.EventArgs) Handles MyBase.Load

    ADODBConnect()
End Sub

Private Sub cn_ConnectComplete(
    ByVal pError As ADODB.Error,
    ByRef adStatus As ADODB.EventStatusEnum,
    ByVal pConnection As ADODB.Connection) Handles cn.ConnectComplete

    '  This is the event handler for the cn_ConnectComplete event raised 
    '  by the ADODB.Connection object when a database is opened.
    Dim x As Integer = 6
    Dim y As Integer = 0
    Try
        x = CInt(x / y) ' Attempt to divide by zero.
        ' This procedure would fail silently without exception handling.
    Catch ex As Exception
        MsgBox("There was an error: " & ex.Message)
    End Try
End Sub

En este ejemplo se genera un error según lo previsto. Sin embargo, si prueba el mismo ejemplo sin el bloque Try...Catch...Finally, el error se omite como si usara la instrucción OnError Resume Next. Sin control de errores, se produce un error en la división por cero de forma silenciosa. Dado que estos errores nunca generan errores de excepción no controlados, es importante usar alguna forma de control de excepciones en controladores de eventos que controlan eventos de objetos COM.

Descripción de los errores de interoperabilidad COM

Sin control de errores, las llamadas de interoperabilidad suelen generar errores que proporcionan poca información. Siempre que sea posible, use el control de errores estructurado para proporcionar más información sobre los problemas cuando se produzcan. Puede resultar especialmente útil al depurar aplicaciones. Por ejemplo:

Try
    ' Place call to COM object here.
Catch ex As Exception
    ' Display information about the failed call.
End Try

Puede encontrar información, como la descripción del error, HRESULT y el origen de los errores COM, examinando el contenido del objeto de excepción.

Problemas de controles ActiveX

La mayoría de los controles ActiveX que funcionan con Visual Basic 6.0 lo hacen con Visual Basic .NET sin problemas. Las excepciones principales son controles de contenedor o controles que contienen visualmente otros controles. Algunos ejemplos de controles anteriores que no funcionan correctamente con Visual Studio son los siguientes:

  • Control Frame de Microsoft Forms 2.0

  • Control Up-Down, también conocido como control de botón de número

  • Control Sheridan Tab

Hay solo algunas soluciones alternativas para los problemas de controles ActiveX no admitidos. Puede migrar controles existentes a Visual Studio si posee el código fuente original. Si no, puede acudir a los proveedores de software para que le proporcionen versiones compatibles con NET actualizadas de los controles a fin de reemplazar los controles ActiveX no admitidos.

Paso de propiedades ReadOnly de controles ByRef

A veces, Visual Basic .NET genera errores COM, como "Error 0x800A017F CTL_E_SETNOTSUPPORTED", cuando se pasan propiedades ReadOnly de algunos controles ActiveX antiguos como parámetros ByRef a otros procedimientos. Las llamadas a procedimiento similares desde Visual Basic 6.0 no generan error y los parámetros se tratan como si se pasaran por valor. El mensaje de error de Visual Basic .NET indica que está intentando cambiar una propiedad que no tiene un procedimiento de propiedad Set .

Si tiene acceso al procedimiento al que se llama, puede evitar este error mediante la palabra clave ByVal para declarar parámetros que aceptan propiedades ReadOnly. Por ejemplo:

Sub ProcessParams(ByVal c As Object)
    'Use the arguments here.
End Sub

Si no tiene acceso al código fuente del procedimiento al que se llama, puede forzar que la propiedad se pase por valor agregando un conjunto adicional de corchetes alrededor del procedimiento de llamada. Por ejemplo, en un proyecto que tiene una referencia al objeto COM de la biblioteca de Objetos de datos ActiveX de Microsoft 2.8, puede usar:

Sub PassByVal(ByVal pError As ADODB.Error)
    ' The extra set of parentheses around the arguments
    ' forces them to be passed by value.
    ProcessParams((pError.Description))
End Sub

Implementación de ensamblados que exponen la interoperabilidad

La implementación de ensamblados que exponen interfaces COM presenta algunos desafíos únicos. Por ejemplo, se produce un posible problema cuando distintas aplicaciones hacen referencia al mismo ensamblado COM. Esta situación es habitual cuando se instala una nueva versión de un ensamblado y otra aplicación sigue usando la anterior. Si desinstala un ensamblado que comparte un archivo DLL, puede hacer que no esté disponible sin querer para los demás ensamblados.

Para evitar este problema, debe instalar ensamblados compartidos en la caché global de ensamblados (GAC) y usar una instancia de MergeModule para el componente. Si no puede instalar la aplicación en la GAC, debe instalarse en CommonFilesFolder en un subdirectorio específico de la versión.

Los ensamblados que no se comparten deben ubicarse en paralelo en el directorio con la aplicación que realiza la llamada.

Consulte también