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
-
Creamos un nuevo proyecto en Visual Studio .NET
(cualquier versión y cualquier lenguaje, o al menos vale para VB y C#)
-
En propiedades del proyecto indicar que es
una librería/biblioteca de clases (DLL)
-
Es conveniente hacer la importación del
espacio de nombres: System.Runtime.InteropServices
-
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#
-
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#
-
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)
-
Si queremos mantener compatibilidad binaria,
deberíamos crear un interfaz con los métodos y propiedades a exponer
desde COM, (pero no los eventos)
-
Esa interfaz la implementamos en el
formulario
-
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.
-
Esa interfaz debería tener asignado el
siguiente atributo:
<InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)> ' VB
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] // C#
-
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.
-
Creamos los métodos, propiedades, eventos,
etc.
-
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.
-
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.
-
(uf, toca madera) Compilamos la aplicación
(recuerda que debe ser una DLL)
-
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.
-
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:
-
Creamos un nuevo proyecto en VB6
-
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.
-
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.
-
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
-
A partir de aquí se usa como el resto de componentes COM:
-
Para crear un nuevo objeto: Set frmNET = New FormNET
-
Para acceder a un método: frmNET.Mostrar
Cosas a tener en cuenta:
-
Siempre debe existir un constructor sin parámetros, ya que COM sólo
utilizará ese constructor y lo necesita.
-
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.
-
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.
-
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.
-
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.
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.
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
|