Exponer una DLL de NetCore 3.1 y usarla con versiones de PowerBuilder anteriores a 2019 (ActiveX/COM)

Hoy traigo buenas noticias para todos los amantes de PowerBuilder que aún no se han actualizado a las versiones de Appeon y que siguen manteniendo aplicaciones con versiones anteriores a PowerBuilder 2017. Este artículo también es interesante para aplicaciones desarrolladas con PowerBuilder 2017, ya que no es hasta la versión 2019 que Appeon nos proporciona el .NET DLL Importer. 

Para estos casos en los que una aplicación antigua debe soportar características modernas, una estupenda opción consiste en escribir la funcionalidad con una herramienta actual y exponerla hacia estas tecnologías antiguas a través de COM/ActiveX.

La web de Microsoft tiene una detallada documentación sobre cómo hacerlo, pero sus contenidos son liosos y sin ningún ejemplo real. Es realmente complicado sacar algo en limpio de ellos. En Google hay mucha información, pero la inmensa mayoría está anticuada o es incompleta. Conseguirlo es bastante sencillo si se sabe cómo, e implica unos pocos pasos a seguir y tener claros algunos conceptos.

En este artículo voy a explicar cómo crear un componente COM/ActiveX sencillo a partir de código .NETCore 3.1 escrito con C#, y luego veremos cómo utilizarlo en PowerBuilder.

Empezamos...

Definir un componente COM o ActiveX con C#

Abre Visual Studio (yo usaré VS2022) y crea un nuevo proyecto C# de tipo "Biblioteca de clases":


Le damos nombre al proyecto con COMServer y elegimos donde guardarlo. 
En mi Caso en C:\Blog\ExpoCom


Elegimos .Net Core3.1 como Framework de nuestra Clase.


Una vez seleccionado el framework, solo tenemos que pulsar el botón de crear.

Definir una interfaz

Debido al funcionamiento interno de COM, toda la funcionalidad que vayas a exponer hacia el exterior deberás hacerlo a través de interfaces. Por ello, antes de nada debes pensar qué métodos quieres exponer hacia el exterior a través de COM y crear una interfaz que los defina. 

Para nuestro ejemplo, expondremos un método llamado ComputePi() para obtener el numero Pi. Utilizo este ejemplo que es el viene en los ejemplos de Microsoft.


Para exponer la Interface al Exterior, .NETCore dispone de un espacio de nombres especializado en la compatibilidad con COM y ActiveX llamado InteropServices.
Por tanto deberemos añadir dicho espacio de nombres y un par de atributos a nuestra interface:


Nota: Puedes definir tantas Interfaces como Objetos quieras exponer en tu clase. En este caso el Objeto que estamos exponiendo sería: "COMServer.Server".

Definir la Clase que implemente la Interface:

Ahora que ya tienes la interfaz con todos los métodos que quieras exponer, en nuestro caso sólo uno, debes crear una clase que la implemente. 
Para ello usarás la sintaxis convencional que consiste en indicar la interfaz a continuación del nombre de clase, separándola con dos puntos.


Al igual que antes hay que añadir el espacio de nombres y los atributos. De tal forma que la clase quedará asi:


Fíjate en dos cosas:

1. He definido explícitamente un constructor sin parámetros, que en realidad no hace nada pues su código está vacío. Esto es necesario porque para instanciar un objeto COM necesitas ese constructor y aparentemente el que crea implícitamente el compilador no nos sirve.

2. Al contrario de lo que verás escrito por ahí en muchos sitios, no es necesario implementar la interfaz explícitamente, aunque en mi ejemplo si lo he hecho.

Además deberás tener en cuenta algunas reglas mínimas que debe cumplir tu clase para que sea compatible:
  • Deberá ser pública, al igual que todos los miembros que quieras exponer.
  • No podrá ser abstracta ni estática.
  • Todos los tipos que se devuelvan o se usen como parámetros deberán ser públicos.
  • No usar tipos muy especializados, como clases genéricas o cosas específicas de .NET, ya que no se soportarán.
  • No podrás usar parámetros opcionales ni valores por defecto.
  • Mientras lo que expongas sea algo mas o menos "normal" no tendrás problemas.
Generar y asignar GUIDs

Tanto la Interface como la Clase deben tener un identificador único para exponerlo. Visual Studio nos proporciona una herramienta para generar estos identificadores que podemos encontrar en el menú herramientas/Crear GUID.

Para este Ejemplo Crearemos la Clase ContractGuids donde guardaremos los identificadores que creemos con la herramienta:



Crearemos dos. Uno para la Interface y otro para la Clase. Tenéis el Botón Nuevo GUID para generar tantos identificadores como queráis y el botón copiar, para no tener que copiarlo a mano...


Nota: Al pegar identificador de la opción 4, eliminaremos los corchetes {}.

Para terminar hay que añadir algunas propiedades a nuestro proyecto:


Nota: Para que sea compatible con versiones antiguas de PowerBuilder hay que compilar la clase a 32bits.

Finalmente, sólo nos queda apretar el botón de run para compilar y probar nuestro proyecto. Como es una biblioteca de clases nos saldrá un mensaje indicando que no se puede iniciar directamente y si nos fijamos en la consola veremos la ruta donde se se han guardado los archivos que necesitaremos copiar mas adelante.



Crear Proyecto de PowerBuilder

Ahora vamos a crear un nuevo proyecto de PowerBuilder. Yo lo voy a hacer en PowerBuilder 2022, que es el que tengo actualmente instalado, pero he testeado con PowerBuilder 11.5, 12.6 y 2017 y funciona de la misma forma lo que vamos a hacer:

Como los lectores de este blog,  están mas familiarizados con PowerBuilder que con CSharp, vamos a resumir mucho mas y poner menos imágenes, para agilizar.

Cremamos un nuevo WorlkSpace llamado ExpoComClient en el directorio donde hemos guardado la solución de Csharp. En Mi caso en C:\Blog\ExpoCom. 

Creamos un nuevo target con el mismo nombre y creamos una ventana a la que llamaremos w_main.
En la ventana crearemos un Botón para llamar a nuestro método:



Copiaremos y pegaremos los siguientes archivos de nuestra solución CSharp en la carpeta donde alojemos el WorkSpace de PowerBuilder:





Registrar el componente para poder usarlo:

Finalmente, para poder utilizar un componente COM/ActiveX es necesario registrarlo en el sistema. No basta con copiarlo y listo, como pasa con las DLL cuando usamos .NET DLL Importer. en versiones superiores a PowerBuilder 2017. 

Para registrar el componente debemos utilizar una herramienta de línea de comandos.
Abre una línea de comandos como administrador y escribe:

regsvr32.exe "c:\Blog\ExpoCom\COMServer.comhost.dll"

Pulsa enter y si todo ha ido bien te saldrá un mensaje de confirmación.


Nota: Para facilitar el proceso de registro he creado 2 botones en PowerBuilder para hacer el registro.
Para que funcionen hay que ejecutar la aplicación con permisos de administrador.

Compilar Demo y Probar:

Finalmente, creamos un proyecto en PowerBuilder para compilar la app y probarla:


Con todo esto que os acabo de explicar, podeis coger cualquiera de mis ejemplos de CSharp hacerlos funcionar con cualquier versión de PowerBuilder antigua.

Os dejo como siempre el enlace de descarga de mi Github:




Fuentes consultadas para escribir este artículo:



Comentarios