Comunicación Entre Ejecutables


Las Referencias a un Unico Objeto Compartido es la Técnica ActiveX de Alto Nivel para Comunicar Datos Entre Ejecutables

Por Harvey Triana

Septiembre 12 de 2000

Una de las cuestiones por las que suelo recibir preguntas es como transferir datos de un ejecutable a otro. La respuesta es simpre la misma: crea un componente fuera de proceso. La documentación de Visual Basic presenta el ejemplo conocido como la clase CoffeeMonitor. No obstante este ejemplo, a parte de extenso, suele parecer complicado y la forma de compartir datos entre diferentes clientes parece poco asible. Desafortunadamente el ejemplo de la MSDN termina la exposición con este titulo : "Un error en la clase Connector", y termina con "...en las aplicaciones de ejemplo Coffee2.Vbp y CoffWat2.Vbp se explora una posible solución a este error"; supongo que algo así desanima a más de uno. El ejemplo que presento en este articulo tiene dos propósitos: (1) Aislar el tratamiento de las clases compartidas (para presentar su funcionalidad claramente), y (2) Solventar el problema: "Un error en la clase Connector".

Este articulo supone que el lector tiene alguna experiencia en la creación de componentes ActiveX.

Introducción

Recordemos que los componentes pueden ejecutarse en proceso o fuera de proceso con respecto a los clientes que utilizan sus objetos. Un componente fuera de proceso, o componente EXE ActiveX, se ejecuta en su propio espacio de direcciones. En virtud de esto, un componente EXE ActiveX puede mantener variables fuera del proceso del cliente. No obstante la implementasion de variables compartidas sugiere una técnica para compartir un objeto para todos los clientes. 

Ejemplo Paso a Paso

El ejemplo que expondré a continuación muestra como la propiedad Message de la clase CMessage se comparte entre diferentes ejecutables. Primero crearemos el componente ActiveX Fuera de Proceso llamado Message.EXE. Luego crearemos un cliente simple de Message.EXE llamado BinTest.EXE.

El Proyecto Message

1. En el menú Archivo, haga clic en Nuevo proyecto.

2. En el cuadro de diálogo Nuevo proyecto, haga doble clic en el icono EXE ActiveX. Asigne Name=Message.

3. A la clase que fue creada como predeterminada daremos el nombre de CMessage (Name=CMessage).

4. Implementamos la clase compartida CMessageStore. Esta clase usa una referencia a un objeto global de un modulo estándar (Shared.BAS). Los modulos estandar en Visual Basic se comportan similar a las clases estáticas de Java, es decir, asi como declaramos Static en un procedimiento, las variables se comparten a nivel de proyecto. La variable gCount mantiene una cuenta lógica de las instancias de la clase CMessage, así el sistema sabe cuando crear o destruir los recursos compartidos. Usamos WithEvents para que podamos procesar eventos de un objeto creado en un modulo estándar (debiera tomar esto como un "tip"). Cada cliente tiene un objeto CMessage, y todos los cliente tienen un unico objeto de MessageStore.
El código para implementar un objeto compartido sule seguir este modelo:

Private WithEvents MessageStore As CMessageStore

Private Sub Class_Initialize()
    If gCount = 0 Then
       Set gMessageStore = New CMessageStore
    End If
    gCount = gCount + 1
    Set MessageStore = gMessageStore
End Sub

Private Sub Class_Terminate()
    If gCount - 1 = 0 Then
       Set gMessageStore = Nothing
    End If
    gCount = gCount - 1
    Set MessageStore = Nothing
End Sub

5.
Creamos el evento MessageChange. Este evento se crea con el propósito de notificar a los clientes que el mensaje a cambiado, en otras palabras, que la propiedad Message ha cambiado de valor. Por supuesto que una de las instancia es la que cambia el mensaje, entones dispara el evento MessageChange en todos los demás clientes activos.

Public Event MessageChange()
...
Private Sub MessageStore_MessageChange()
    RaiseEvent MessageChange
End Sub

6.
Creamos la propiedad Message. Esta propiedad representa una variable compartida. Note que quien almacena la variable es la clase compartida CMessageStore, y CMessage

Public Property Get Message() As String
    Message = MessageStore.Message
End Property

Public Property Let Message(RHS As String)
    MessageStore.Message = RHS
End Property

7.
Algun codigo de prueba. La propiedad Count nos dira el numero de instancia de la clase CMessage creada por los clientes. 

'//Code Test
Public Property Get Count() As Long
    Count = gCount
End Property

8. En total, el código de la clase CMessage es el siguiente:

'------------------------------------------------
' NAME : CMessage
' TYPE : Class, Instancing=MultiUse
' PROJECT : Messages, EXE ActiveX
' AUTHOR : Harvey T.
' DESCRIPTION : Keeps shared class CMessageStore
' UPDATE : -
'------------------------------------------------
Option Explicit

Public Event MessageChange()

Private WithEvents MessageStore As CMessageStore

Private Sub Class_Initialize()
    If gCount = 0 Then
       Set gMessageStore = New CMessageStore
    End If
    gCount = gCount + 1
    Set MessageStore = gMessageStore
End Sub

Private Sub Class_Terminate()
    If gCount - 1 = 0 Then
       Set gMessageStore = Nothing
    End If
    gCount = gCount - 1
    Set MessageStore = Nothing
End Sub

Private Sub MessageStore_MessageChange()
    RaiseEvent MessageChange
End Sub

Public Property Get Message() As String
    Message = MessageStore.Message
End Property

Public Property Let Message(RHS As String)
    MessageStore.Message = RHS
End Property

'//Code Test
Public Property Get Count() As Long
    Count = gCount
End Property


9.
El modulo Shared. Cree un modulo estándar y de el valor Shared a la propiedad Name. El código de este modulo es el siguiente:

'------------------------------------------------
' NAME : Shared
' TYPE : Standard Module
' PROJECT : Messages, EXE ActiveX
' AUTHOR : Harvey T.
' DESCRIPTION : Keeps shared data
' UPDATE : -
'------------------------------------------------
Option Explicit

Public gCount As Long
Public gMessageStore As CmessageStore

10.
La clase CMessageStore. Esta es la clase compartida, es quien realmente mantiene la variable compartida (m_Message). El código es el siguiente:

'------------------------------------------------
' NAME : CMessageStore
' TYPE : Class, Instancing=Private
' PROJECT : Messages, EXE ActiveX
' AUTHOR : Harvey T.
' DESCRIPTION : This is the shared class
' UPDATE : -
'------------------------------------------------
Option Explicit

Public Event MessageChange()

Private m_Message As String

Public Property Get Message() As String
    Message = m_Message
End Property

Public Property Let Message(RHS As String)
    m_Message = RHS
    RaiseEvent MessageChange
End Property

11.
Por ultimo genere el ejecutable Message.EXE.

El Proyecto de Prueba

1. En el menú Archivo, haga clic en Nuevo proyecto.

2. En el cuadro de diálogo Nuevo proyecto, haga doble clic en el icono EXE Estándar. Daremos al proyecto el nombre de BinTest (Name= BinTest)

3. Al formulario predeterminado demos el nombre de frmBinTest

4. Agreguemos un CommandButton (Name=cmdSend), un segundo CommandButton (Name= cmdTestCount) y un TextBox (Name= txtMessage). La disposición de los controles es la siguiente:

5. Agreguemos la referencia al componente Message creado anteriormente: Desde el Menú Proyecto, Opción: Referencias. Ubique el archivo Message.EXE (puede ser mas cómodo hacerlo desde el botón Examinar).

6. Agregue el siguiente código al formulario frmBinTest:

'------------------------------------------------
' NAME : frmBinTest
' TYPE : Form
' PROJECT : BinTest, EXE Standard
' AUTHOR : Harvey T.
' DESCRIPTION : Sample of Shared Class
' UPDATE : -
'------------------------------------------------
Option Explicit

Private WithEvents msg As Messages.CMessage

Private Sub cmdSend_Click()
    txtMessage.SetFocus
    msg.Message = txtMessage.Text
End Sub

Private Sub cmdTestCount_Click()
    txtMessage.Text = "Count = " & msg.Count
End Sub

Private Sub Form_Load()
    Set msg = New Messages.CMessage
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set msg = Nothing
End Sub

Private Sub msg_MessageChange()
    If Not txtMessage.Text = msg.Message Then
       txtMessage.Text = msg.Message
    End If
End Sub

7.
Por ultimo genere el ejecutable BinTest.EXE


Prueba

Abra el ejecutable BinTest.EXE las veces que desee. Ahora escriba algo en la caja de texto y use el comando Send. Vera que todos los cliente reconocen el mensaje. La siguiente imagen fue mi prueba:


Use el comando TestCount para reflejar el numero de instancia de la clase CMessage (cierre y abra nuevas instancias de BinTest.EXE para mas claridad).


Utilización en el Mundo Real 

Por supuesto la aplicación BinTest no el único cliente que puede utilizar Message.EXE, lo pueden utilizar en otros programas creados con Visual Basic, y en general todo lo que siga la norma ActiveX, por ejemplo cualquier aplicación de Office, programas creados con J++, etc.

Quizás los servicios de compartir variables entre ejecutables sea mas interesante en un entorno de network o en el Web. Por supuesto aquí es donde Ud. Debe crear un componente distribuido. El código no es diferente, el despliegue en red si que lo es.