Hoy voy a tratar un tema ampliamente tratado ya, pero que no está muy unificado en la forma de plantearlo: La generación de DLLs en .Net y el consumo desde Visual FoxPro como objeto COM. Y no significa que este artículo plantee una unificación de todas las metodologías que se encuentran. Todo lo contrario, es otro caso particular, basado en mi experiencia personal sobre el tema. Simplemente ocurre que tengo planeado escribir algunos artículos sobre la interoperabilidad entre .Net y VFP, y estas especificaciones van a repetirse en todos ellos. Igualmente, este es un tema que se presta a experimentos de prueba y error, así que este artículo también puede servir como una alternativa más en la búsqueda de este objetivo que, al principio, genera sus dolores de cabeza, pero luego muestra sus beneficios y permite la escalabilidad de los sistemas unificando tecnologías que aporten, cada una, su mejor parte.
Antes que nada, les dejo algunos links que tratan del tema:
Otro paso importante, antes de poner manos a la obra, es describir el entorno sobre el que fue desarrollado este caso de
prueba:
- Microsoft Visual Studio 2010 Express: para desarrollar la librería de clases. Esto es necesario solamente en la máquina donde se compile la DLL. Se puede hacer con versiones anteriores. Los pasos que se explican en el resto del documento son idénticos para la versión 2005 y posteriores.
- Framework de .Net 4.0: Esto se necesita tanto en la máquina donde se compila como en la del cliente que vaya a consumir la DLL. Se puede hacer con versiones anteriores (al menos, con la versión 2.0 puedo afirmar que funciona).
- Visual FoxPro 9.0
Del lado desde el que se compila la DLL
Empezamos por crear una librería de clases con alguno de los lenguajes de .Net (C# para este ejemplo). Es importante que exista al menos una clase pública en la librería, para poder consumirla como objeto COM. A continuación se muestra un ejemplo básico de una clase con un atributo privado llamado propiedad el cual se manipula mediante otro miembro, pero de alcance público, llamado Propiedad (C# .Net diferencia mayúsculas y minúsculas):
namespace LibreriaNET
{
public class ClaseNet
{
private string propiedad;
public string Propiedad
{
get { return this.propiedad; }
set { this.propiedad = value; }
}
}
}
Luego, es necesario realizar ciertos pasos de configuración sobre las propiedades del ensamblado que se va a generar. Para esto, ingresamos en la sección de propiedades de la librería, ubicada en el explorador de soluciones.
Todo cambio que se realice en la pantalla que va a aparecer se verá reflejado en el archivo AssemblyInfo.cs. Por lo tanto es indistinto trabajar sobre este archivo o sobre la interfaz gráfica.
La primer sección de las propiedades es Aplicación. Una de las características importantes de esta sección es la propiedad Nombre del ensamblado que es el nombre de la DLL, y se va a utilizar a la hora de instanciar las clases. Puede o no coincidir con otras propiedades como espacio de nombres predeterminado, título del ensamblado, etc.
En la misma sección, si se presiona sobre el botón Información del Ensamblado, se verá una pantalla con opciones como Título, Descripción, etc. La opción que nos interesa es Crear ensamblado visible a través de COM, la cual debe quedar seleccionada.
Esta acción es idéntica a modificar el archivo AssemblyInfo.cs para que incluya la siguiente línea:
[assembly: ComVisible(true)]
Con esto ya se logra que la librería pueda utilizarse como objeto COM. Sin embargo, es posible que las aplicaciones que quieran consumir la DLL, no encuentren la referencia a dicha librería. Una solución es registrarla en una especie de repositorio de librerías que maneja el framework de .Net, llamado GAC (Global Assembly Cache). Para esto, es necesario que la librería cuente con una firma que la identifique dentro del repositorio. Esta firma se genera como un archivo con extensión SNK. En la mayoría de los artículos que consulté, la opción utilizada es por línea de comandos, mediante sn -k firma.snk y luego se asocia al proyecto de .Net. En mi caso, suelo usar la interfaz gráfica directamente (no encontré diferencias).
Desde la sección Firma dentro de las propiedades del ensamblado se debe seleccionar la opción Firmar el ensamblado y seleccionar un archivo SNK del combo (o elegir crear una nueva firma).
Con esto se genera el archivo SNK y se asocia al proyecto. Solamente queda generar la DLL y el trabajo en la máquina en la que se genera la librería ya está terminado. Desde ahora, cada vez que se necesite modificar algo, no será necesario rehacer todos los pasos. Simplemente se cambia el código fuente y se vuelve a generar la DLL. Las propiedades y la firma se mantienen.
Del lado desde el que se consume la DLL
Ahora queda explicar la distribución de la DLL a cualquier máquina que no necesita contar con Visual Studio, pero sí con el framework de .Net, más precisamente para utilizar el comando RegAsm, usado para registrar la librería. Desde la línea de comandos y ubicados en la ruta donde se encuentra el archivo DLL, se ejecuta el siguiente comando:
c:\rutaDLL\>c:\WINDOWS\Microsoft.Net\Framework\V4.0.30319\RegAsm libreriaNet.dll /register /codebase /tlb
La ruta del comando RegAsm puede variar según la instalación del framework. El parámetro /codebase es el que indica que la DLL se va a registrar en la GAC, y esto es posible porque la librería fue firmada con un archivo SNK.
Cada vez que se quiera implementar una nueva versión, se deberá ejecutar nuevamente el mismo comando. Por otro lado, si se necesita, por alguna razón, remover la DLL del registro, se utiliza el comando regasm libreriaNet.dll /u.
Y ya solamente queda probar a consumir la clase creada. Desde Visual FoxPro se puede hacer con las siguientes líneas:
objNet = CREATEOBJECT("LibreriaNet.ClaseNet")
objNet.Propiedad = "Hola Mundo"
?objNet.Propiedad
Si no hay error, la ejecución debería ser exitosa, creando el objeto, seteando el valor de su atributo "propiedad" y mostrándolo por pantalla.
PabloLissa
Unas aclaraciones importantes:
- Visual FoxPro no permite instanciar objetos de .Net utilizando un constructor con parámetros. Es decir, solamente permite utilizar el constructor por defecto de la clase.
Lo extraño es que una línea como la siguiente:
obj = CREATEOBJECT("LibreriaNet.ClaseNet", 1, .F., .T., "Parámetro")
no arroja ningún error, lo que daría a pensar que si la clase tiene definido un constructor con 4 parámetros, el objeto estaría correctamente instanciado, pero no es así. En este caso, se omiten los parámetros y se ejecuta el constructor por defecto.
- No se puede utilizar la sobrecarga de métodos. Si existen dos métodos con distinto número de parámetros, solamente se puede invocar al que se encuentre primero en el código fuente. Por ejemplo, si en la definición de la clase se hubiese incluido:
public void funcion1(string s, string s2) { }
public void funcion1(string s) { }
solamente se podría usar la primera, con dos parámetros. La otra fallaría, mostrando el error "Código de estado COM desconocido"
Si, en cambio, se hubiese definido:
public void funcion1(string s) { }
public void funcion1(string s, string s2) { }
la función con un parámetro sería la que responda correctamente.