martes, 20 de agosto de 2013

Conexión DDE entre .NET y VB6

El artículo de hoy trata sobre un tema un poco desfasado en el tiempo pero que puede resultar útil en compañías donde todavía se trabaje con aplicaciones desarrolladas en VB6 o similares. En realidad nuestra necesidad surge de comunicar una aplicación VB6 contra una centralita telefónica teniendo en cuenta que ya tenemos desarrollada este tipo de comunicación en las aplicaciones .NET de la compañía.

Tal vez podríamos haber optado por publicar un servicio REST en .NET y crear un cliente desde VB6, pero teniendo en cuenta que la aplicación .NET que comunica con la centralita es de escritorio, la solución implicaba tener que montar un servidor web... a parte la comunicación tiene que ser bidireccional por lo que sólo se me ocurre tener que estar haciendo peticiones constantemente a ver si el servidor tiene novedades para el cliente lo cual resulta de todo menos óptimo. También barajamos extraer las funcionalidades de la dll que conecta con la centralita y hacerla interoperable COM pero tras varias pruebas descartamos esa solución (...) por resultar un auténtico curro y engorro hacer la dll interoperable con todos sus tipos y clases.

Tal vez la mejor solución que se nos ocurre a la mayoría es tirar la aplicación VB6 a la basura y rehacerla en .NET jajjaja, pero fuera bromas, en este escenario no es posible así que decidimos probar con una comunicación

DDE

entre las aplicaciones. El resultado es que en unas horas teníamos implementada la comunicación sin conocer a fondo el protocolo y sin perder demasiado tiempo. De hecho sólo hemos implementado un par de funcionalidades, llamar a un número desde VB6 (VB6 a .NET) y avisar a la aplicación de VB6 que tiene una llamada entrante (.NET a VB6).

Resumiendo, ambas aplicaciones han de asumir el rol de servidor o cliente en determinados momentos. A continuación intento reproducir los pasos que han sido necesarios para desarrollar este tipo de comunicación.

En primer lugar comentar que .NET no soporta protocolo

DDE

, aquí os paso un dll de CodePlex con un proyecto desarrollado por terceros a través del cual podemos trabajar con este protocolo en aplicaciones .NET. Una vez descargamos el proyecto nos quedamos con el archivo NDde.dll y lo agregamos como referencia a nuestro proyecto .NET.

.NET a VB6

  • Servidor (VB6):

    En nuestro caso necesitamos pasar a VB6 una cadena de texto a la que la aplicación reaccionará abriendo un formulario de aviso con los datos de la persona que está llamando. Tenemos un formulario principal en esta aplicación por lo que sólo ha sido necesario modificar las propiedades "LinkTopic = 'AreaTICTopic'" y "LinkMode = 1-Source del Form. Si implementamos el evento "Form_LinkExecute(CmdStr As String, Cancel As Integer)" del formulario ya tenemos el servidor VB6 listo para recibir comandos desde .NET.

  • Cliente (.NET):

    Os paso el código necesario para enviar un comando al servidor Vb6.
    using (DdeClient client = new DdeClient("NombreAppVisualBasic", "AreaTICTopic"))
    {
       client.Connect();
       client.Execute(string.Format("Llamada entrante del número {0}", pTelefono), 60000);
       client.Disconnect();
    }
    

VB6 a .NET

  • Cliente (VB6):
    Private Sub cmdLlamar_Click()
        'llamar a DDE .NET informando número de teléfono
        txtnumero.LinkMode = vbLinkNone
        txtnumero.LinkTopic = "AreaTICDDEServer|CallFromVB"
        txtnumero.LinkMode = vbAutomatic
        txtnumero.LinkExecute txtnumero.Text
    End Sub
    
  • Servidor (.NET):

    Tenemos que crearnos una clase AreaTICDDEServer la cual implemente la clase abstracta "DDeServer" y sobreescribiremos algunos métodos para controlar los comandos que nos llegan desde el cliente. Ya digo no he invertido demasiado tiempo en conocer el protocolo así que si queréis indagar más vale que miréis a fondo los ejemplos de codeplex porque hay varios métodos más en la clase abstract que tal vez os interese implementar... lo mío era salir del paso. Los eventos OnError y OnLlamada son opcionales es tema de como organizo el código para no tener que mezclar esta clase con la otra que realiza la llamada lo gestiono con eventos y delegados.
    namespace AreaTIC.DDE
    {
        public sealed class AreaTICDDEServer : DdeServer
        {
            public AsteriskDDEServer(string service)
                : base(service)
            {
            }
    
            public event EventHandler OnError;
            public event EventHandler OnLlamada;
    
            public override void Register()
            {
                base.Register();
            }
    
            public override void Unregister()
            {
                base.Unregister();
            }
    
            protected override bool OnBeforeConnect(string topic)
            {
                try
                {
                    if (!Enum.IsDefined(typeof(Topics), topic))
                    {
                        throw new Exception("No se reconoce el valor de 'Topic' de la conexión entrante");
                    }
    
                    return true;
                }
                catch (Exception ex) 
                {
                    this.OnError(this, new DDEErrorEventArgs(ex));
                    return false;
                }
            }
    
            protected override ExecuteResult OnExecute(DdeConversation conversation, string command)
            {
                if (!Enum.IsDefined(typeof(Topics), conversation.Topic))
                {
                    throw new Exception("No se reconoce el valor de 'Topic' de la conexión entrante");
                }
                else 
                {
                    Topics topic = (Topics)Enum.Parse(typeof(Topics), conversation.Topic);
                    switch (topic) 
                    { 
                        case Topics.CallFromVB:
                            this.OnLlamada(this, new CallEventArgs(command));
                            break;
                    }
                }
    
                // Tell the client that the command was processed.
                return ExecuteResult.Processed;
            }
        }
    
        public enum Topics
        {
            CallFromVB,
        }
    
        public class DDEErrorEventArgs : EventArgs 
        {
            public DDEErrorEventArgs(Exception pex) 
            {
                ex = pex;
            }
    
            public Exception ex { get; set; }
        }
    }
    
    Para iniciar el servidor DDE desde .NET sería necesario este código.
        AreaTICDDEServer mDDEserver = new AreaTICDDEServer("AreaTICDDEServer"); 
        mDDEserver.Register();
        mDDEserver.OnLlamada += this.Llamar;
        mDDEserver.OnError += this.ErrorDDE;
    
Con este ya tendríamos implementada una comunicación

DDE

entre las 2 aplicaciones capaz de intercambiar comandos entre ambos procesos. Hasta aquí el artículo de hoy, recordar que podéis seguir

areaTIC

en las principales redes sociales y no os cortéis a comentar, preguntar o valorar las publicaciones!

No hay comentarios:

Publicar un comentario en la entrada