Ir a la sección de .NET Cómo... en .NET

Usar un formulario de .NET desde VB6

Resumen de crear un componente de .NET para usar desde COM

 
Publicado el 29/Nov/2006
Actualizado el 29/Nov/2006
Autor: Guillermo 'guille' Som

Cómo usar un formulario creado con una aplicación de .NET desde Visual Basic 6.
Esto también te servirá como resumen para crear un componente de .NET para usar desde COM.
Con código de ejemplo tanto para Visual Basic como para C#.


Introducción:

Este es un resumen de los pasos que deberíamos dar para crear un componente de .NET para poder usarlo desde cualquier lenguaje que utilice COM (o ActiveX), como puede ser Visual Basic 6.

Estos pasos forman parte de unas explicaciones para poder crear una DLL de .NET en la que se utiliza un formulario para poder usarlo desde Visual Basic 6.0, también he añadido ciertos puntos a tener en cuenta si se quiere mantener la compatibilidad binaria con VB6, con idea de que se pueda modificar el componente de .NET sin necesidad de tener que recompilar los programas de Visual Basic 6 que utilicen versiones anteriores de ese componente.
Entre esos "consejos" está la forma de añadir nuevos eventos al componente sin romper esa compatibilidad binaria.

Si quieres conocer más detalles sobre esto de crear componentes de .NET desde COM, puedes leerte esta serie de tres artículos que publiqué en Enero de 2003: Usar un componente .NET desde COM.

 

Nota:
Recuerda que todo esto es una especie de "chuleta" que es la que suelo usar yo cuando voy a fabricarme algún componente de .NET para usar desde Visual Basic 6.

Y aunque aquí se hable de formularios, es aplicable a cualquier clase.
 

 

Los pasos a dar para crear el componente de .NET

  1. Creamos un nuevo proyecto en Visual Studio .NET (cualquier versión y cualquier lenguaje, o al menos vale para VB y C#)

  2. En propiedades del proyecto indicar que es una librería/biblioteca de clases (DLL)

  3. Es conveniente hacer la importación del espacio de nombres: System.Runtime.InteropServices

  4. En el fichero AssemblyInfo, además de rellenar los atributos (recomendable, aunque no obligatorio), deberíamos usar el siguiente atributo para poder manejarnos bien con el VB6:
    <Assembly: ClassInterface(ClassInterfaceType.None)> ' VB
    [assembly: ClassInterface(ClassInterfaceType.None)] // C#

  5. Debemos firmar el ensamblado con nombre seguro:
    <Assembly: AssemblyKeyFileAttribute("... el path al fichero de claves .snk")> ' VB
    [assembly: AssemblyKeyFileAttribute("... el path al fichero de claves .snk")] // C#

  6. En VB no hace falta, pero en C# creo que no lo añade automáticamente, por tanto en ese mismo AssemblyInfo deberíamos asignar un atributo para el GUID a usar desde COM:
    <Assembly: Guid("NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNN")> ' VB
    [assembly: Guid("NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNN")] // C#
    (usa la herramienta guidgen.exe que estará en Common7/Tools del directorio de instalación de Visual Studio)

  7. Si queremos mantener compatibilidad binaria, deberíamos crear un interfaz con los métodos y propiedades a exponer desde COM, (pero no los eventos)

  8. Esa interfaz la implementamos en el formulario

  9. Si queremos que nuestro componente tenga eventos visibles en COM deberíamos definir una interfaz para los eventos, estos se declaran como Sub (void en C#) con los parámetros (argumentos) que necesite cada uno.

    1. Esa interfaz debería tener asignado el siguiente atributo:
      <InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)> ' VB
      [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] // C#

    2. Para que la clase (el formulario) utilice esa interfaz para los eventos, no hace falta implementarla, pero sí debemos asignar el siguiente atributo:
      <ComSourceInterfaces("espacioDeNombres.NombreDeLaInterfazDeEventos")> ' VB
      [ComSourceInterfaces("espacioDeNombres.NombreDeLaInterfazDeEventos")] // C#
      Así desde el VB6 podremos declarar la variable con WithEvents.
       

  10. Creamos los métodos, propiedades, eventos, etc.

  11. IMPORTANTE, el formulario debe tener un constructor sin parámetros,  (ya lo tiene por defecto), lo aclaro, por si a alguien se le ocurre la brillante idea de quitar el que viene por defecto y declarar uno que reciba parámetros.

  12. Cuando creemos el objeto desde VB6 (el formulario de .NET) no se mostrará, por tanto deberíamos tener un método que muestre el formulario.

  13. (uf, toca madera) Compilamos la aplicación (recuerda que debe ser una DLL)

  14. Nos vamos al directorio en el que hemos creado la DLL, abrimos una ventana de comandos (MS-DOS) con el path a las herramientas de .NET y creamos un TLB para usar desde COM:
    regasm nombreDeLaLibrería.dll /tlb
    Esto hará que se registre las interfaces de la librería.

  15. Guardamos la librería en el GAC (podemos hacerlo manualmente arrastrándola a la carpeta Windows\assembly) o bien usar la herramienta gacutil.exe:
    gacutil /i nombreDeLaLibrería.dll
     

 

Nota:
En Windows Vista debemos abrir la ventana de consola con permisos de administrador, sino, no nos dejará modificar el registro.

 

Ya está la librería registrada y lista para usar desde VB6.

Usar la DLL desde Visual Basic 6

Ahora nos vamos a VB6:

  1. Creamos un nuevo proyecto en VB6

  2. En Proyecto>Referencias buscamos nuestra DLL (el nombre será el que le hayamos dado en la descripción del AssemblyInfo del proyecto .NET) y la marcamos.

  3. Creamos una variable a nivel de módulo (para que esté visible en todo el formulario), aunque se puede hacer de forma privada, pero así podemos declararla con WithEvents.

    1. Es importante saber que si el nombre de la DLL (que será el que se use desde COM) tiene puntos, estos serán reemplazados por un guión bajo.
      Por ejemplo si el nombre del ensamblado se llama: elGuille.COM.pruebaFrm, en COM se verá como: elGuille_COM_pruebaFrm
      Después de este nombre (que será como el de una librería de COM), vendrá el nombre (o nombres) de la clase (el formulario)
      En el caso de que el formulario se llame FormNET, (es recomendable usar otro nombre que el predeterminado), para crear la clase haríamos:
      Private WithEvents frmNET As elGuille_COM_pruebaFrm.FormNET
       

  4. A partir de aquí se usa como el resto de componentes COM:

    1. Para crear un nuevo objeto: Set frmNET = New FormNET

    2. Para acceder a un método: frmNET.Mostrar
       

 

Cosas a tener en cuenta:

  1. Siempre debe existir un constructor sin parámetros, ya que COM sólo utilizará ese constructor y lo necesita.

  2. Los arrays siempre se deben pasar por referencia, se pueden pasar desde VB6 a .NET y desde .NET a VB6, incluso como valor devuelto por un método o propiedad.

  3. Si después de distribuir la aplicación de VB6 cambiamos los métodos o propiedades del formulario de .NET, DEBEMOS crear una nueva interfaz que contenga los nuevos métodos y dejar la que ya teníamos, esto es para mantener la compatibilidad binaria y no tener que recompilar el ejecutable ya distribuido.
    A la hora de implementar esas interfaces, (en la aplicación de .NET) poner siempre la nueva antes que las anteriores.

  4. Si queremos añadir nuevos eventos... bueno... reza aunque seas ateo...
    Si se añaden a la interfaz con los métodos (equivalentes) a los eventos, habrá que recompilar el ejecutable de VB6, ya que se pierde la compatibilidad binaria.
    Si no se añaden a esa interfaz, se puede seguir usando el ejecutable de VB6 sin necesidad de recompilar, pero como es lógico no se podrán usar tampoco en nuevas "recompilaciones" del exe de VB6.

  5. El nombre de la DLL (el ensamblado compilado) es el que se usará como nombre "contenedor" de las clases desde VB6 y que si ese nombre tiene puntos, éstos se cambiarán por guión bajo (con otros caracteres no he probado, pero supongo que siempre los cambiará a caracteres aceptados por COM)
     

Por supuesto,
lo que NUNCA debemos hacer es quitar eventos o cualquier otro miembro de las interfaces
.
Ya que esas interfaces son las que se usan desde COM y si la interfaz "no concuerda", pues... error al canto.
Todo esto sólo es aplicable al ejecutable de VB6, ya que desde .NET si que se podrán usar.

 

Más notas para mantener la compatibilidad binaria al añadir nuevos eventos

Para mantener la compatibilidad binaria al añadir nuevos eventos, hay que crear una nueva interfaz con los eventos que queremos usar desde VB6 (en la nueva interfaz estarán TODOS los eventos que queramos exponer, es decir podemos usar los anteriores y los nuevos o quitar de los anteriores, etc.).

Y debemos usar este atributo ComSourceInterfaces de la siguiente forma:

Para Visual Basic:

<ComSourceInterfaces(GetType(interfazNueva), GetType(interfazAnterior))> _
Public Class ...

Para C#:

[ComSourceInterfaces(typeof(interfazNueva), typeof(interfazAnterior))]
public class ...

Esto nos permite hasta 4 modificaciones en las interfaces de eventos.
La nueva interfaz de eventos debe ser la primera que se indique en el atributo.

Si no ponemos la interfazAnterior se romperá la compatibilidad binaria.

Y por supuesto, los nuevos eventos sólo se podrán usar en los nuevos ejecutables que compilemos (como es lógico), ya que en los anteriores... pues no se sabía de su existencia, así que no se usarán.

Si necesitas crear más de 4 interfaces para seguir manteniendo la compatibilidad binaria con los eventos, tendrás que usar el atributo ComSourceInterfaces pasándole al constructor una cadena con esas interfaces, pero separándolas con un carácter nulo (ChrW(0) en VB '\0' en C#).

Por ejemplo, el código anterior lo podemos indicar también así:

Para Visual Basic:

<ComSourceInterfaces("interfazNueva" & ChrW(0) & "interfazAnterior")>

Para C#:

[ComSourceInterfaces("interfazNueva\0interfazAnterior")]

Como nota final, decir que tanto interfazNueva como interfazAnterior deben ser los nombres completos, es decir, incluyendo los espacios de nombres en los que estén alojados.

 

El código de ejemplo

El código de la DLL de ejemplo está hecho con Visual Studio .NET 2003 y en los "zips" se incluyen las DLL tanto para Visual Basic como para C#, así como los dos ejecutables que usan esas DLL desde COM, que están hechos con Visual Basic 6.0 Professional con el Service Pack 6.

En los ficheros comprimidos (RAR) se incluyen tanto las aplicaciones de .NET como las de Visual Basic 6.0.

El funcionamiento es muy simple, en ambos proyectos tenemos un formulario con un ListBox, el de .NET lo puedes cambiar de tamaño y los controles se adaptan al nuevo tamaño (usando Anchor).
Desde el formulario de VB6 se puede mostrar el de .NET y al mostrarse, tendrá los mismos elementos en el ListBox que tenía el de VB6, cada vez que modifiques los que tiene el formulario de .NET se modificarán los de VB6, ya que cada vez que se modifica el contenido del ListBox, se lanza un evento que desde VB6 lo interceptamos para saber que es lo que está ocurriendo.

En la figura 1 tienes una captura de la aplicación en ejecución desde Windows Vista.

 

La aplicación compilada de VB6 con el formulario de .NET
Figura 1. La aplicación compilada de VB6 con el formulario de .NET

 

En la figura 2 tienes el examinador de objetos de Visual Basic 6, en el que puedes ver los métodos y eventos que expone la DLL creada con .NET.

 

El examinador de objetos de VB6 con el contenido de la DLL de .NET
Figura 2. El examinador de objetos de VB6 con el contenido de la DLL de .NET

 

Espero que todo esto te sea de utilidad, lo mismo que lo es para mi.

Nos vemos.
Guillermo

 


Código de ejemplo (comprimido):

 

El código de la DLL de .NET (VB): frmVBParaCOM.rar - 16.0 KB
(MD5: DEB327FBF2176C4B73C2A695AE0AD4D9)

El código de VB6 que usa la DLL creada con VB: tVB6frmVBParaCOM.rar - 7.39 KB
(MD5: 6B895A9B24A2FB14759C97C3D61F6A6D)

El código de la DLL de .NET (C#): frmCSparaCOM.rar - 15.4 KB
(MD5: 3D573E2E96E1A2B229D6257CA7DCD882)

El código de VB6 que usa la DLL creada con C#: tVB6frmCSparaCOM.rar - 7.57 KB
(MD5: 9E99E03E4FDBFE76DBDBFBD67AA8E8C9)

(Pulsa aquí para saber de que va eso del MD5 checksum)

 


Ir al índice principal de el Guille

Valid XHTML 1.0 Transitional  ¡CSS Válido!