VB6 – Aplicación visual que se reinicia sola

Esta entrada trata sobre conseguir que una aplicación se reinicie solas o como la iba a llamar en un principio, sobre “el script mágico”.

A veces te encuentras desarrollando una aplicación, la cual necesitas que este constantemente en ejecución.
Por ejemplo, una aplicación que monitoria un directorio, lee constantemente un registro, una aplicación de telemetría, etc.
Pero ya sea por una falta de un correcto o suficiente control de errores o/y excepciones. A la aplicación le surge “errores” que la bloquean o cierran de forma inesperada, creando una sensación de inestabilidad.
En esta entrada quiero mostraros una manera de como intentar aliviar esto y conseguir que nuestra aplicación se recupere por si sola.

Bien… Por ejemplo, partimos de una aplicación que monitoria un sistema.
La aplicación esta constantemente en ejecución, día y noche. Por tanto, es imposible estar en persona verificando el estado de la aplicación.

Primero debemos hacer nuestro trabajo lo mejor posible…
Controlan todos los errores posibles que pueden surgir en la ejecución, ya sea mediante “decisiones” en tiempo de ejecución (chequeos con “if, while, etc…”) o mediante el control de errores (“On Error Goto…”, “On Error Resume”, etc…).

Bueno… hemos hecho un trabajo de control de errores lo mas exhaustivo posible. Pero llegamos a algún error que, por distintas razones, no sabemos por donde atajarle.
Yo a veces opto por añadir el comando “Stop”, así intentar depurar el error y estudiare de manera particular (pero bueno eso es otro historia…).

Pero si aun así no se soluciona y la única opción que se nos ocurre es cerrar la aplicación (o matar el proceso) y volver a ejecutarla.

Pero, supongamos… ¿Y si la aplicación se da cuenta que llega a un punto sin retorno y ella misma se cierra y se relanza por si misma?

¿Como lo conseguimos?
Muy sencillo, justamente antes de que la aplicación se cierre, ella lanza un proceso que se encargue de relanzar de nuevo la aplicación.

Debe ser un proceso independiente ya que el mismo proceso de la aplicación no puede relanzarse (seria un contrasentido que uno mismo se cerrara y se re-arrancara, a mi parecer algo muy complicado).

Este proceso puede ser cualquier tipo de aplicación, otra aplicación visual, un batch, etc… aunque últimamente yo me inclino por un vbscript.
Vbscript porque utilizamos el mismo lenguaje visual basic. Solo necesito un editor de texto, ni compilar ni nada. Algo muy comodo y rapido.

Dentro de nuestra aplicaciones insertamos la llamada al procedimiento, por ejemplo en las lineas finales del control de errores (cuando ya no tenemos mas opciones):

'
On Error GoTo 0
'
Exit Sub
'
Manejar_Errores_Apertura_01:
'MsgBox "Error al intentar abrir COM" & Connect_RS232.CommPort, vbCritical, "mdlRS232 [Evento_01_Timer_RS232]"
'MsgBox "Error detectado por Visual Basic: " & vbCrLf & Err.Description, vbCritical, "mdlRS232 [Evento_01_Timer_RS232]"
'
Call Log("ERROR_Timer_RS232_Timer_01 - Error al intentar abrir COM" & Connect_RS232.CommPort)
Call Log("ERROR_Timer_RS232_Timer_01 - Error detectado por Visual Basic: " & vbCrLf & Err.Description)
'
Resume Salir
''
Manejar_Errores_Transmision_01:
'MsgBox "Ocurrió un error al intentar transmitir"
'MsgBox "Error detectado por Visual Basic: " & vbCrLf & Err.Description, vbCritical, "mdlIberia_RS232 [Evento_01_Timer_RS232]"
'
Call Log("ERROR_Timer_RS232_Timer_02 - Ocurrió un error al intentar transmitir")
Call Log("ERROR_Timer_RS232_Timer_02 - Error detectado por Visual Basic: " & vbCrLf & Err.Description)
'
If (Connect_RS232.PortOpen = True) Then Connect_RS232.PortOpen = False
'
Resume Salir
''
Manejar_Errores_Clausura_01:
'MsgBox "Error al intentar cerrar COM" & Connect_RS232.CommPort, vbCritical, "mdlRS232 [Evento_01_Timer_RS232]"
'MsgBox "Error detectado por Visual Basic: " & vbCrLf & Err.Description, vbCritical, "mdlRS232 [Evento_01_Timer_RS232]"
'
Call Log("ERROR_Timer_RS232_Timer_03 - Error al intentar cerrar COM" & Connect_RS232.CommPort)
Call Log("ERROR_Timer_RS232_Timer_03 - Error detectado por Visual Basic: " & vbCrLf & Err.Description)
'
Resume Salir
''
Salir:
' AQUI PUEDO PONER ALGO QUE QUIERA QUE SE EJECUTE
' EN CUALQUIER SITUACIÓN
'Stop
On Error GoTo 0
'
Call Reinicio_Aplicacion_01(App.EXEName & ".exe", App.EXEName & ".exe " & Command, True, GetCurrentProcessId)
End
''
End Sub

El procedimiento…

Public Sub Reinicio_Aplicacion_01(ByVal strP_Proceso_01 As String, ByVal strP_Aplicacion_01 As String, _
Optional ByVal bolP_Control_ID_01 As Boolean = False, _
Optional ByVal lonP_ID_Proceso_01 As Long)
'
'---------------------------------------------'
' FUNCION PARA EL REINICIO DE UNA APLICACION  '
'                                             '
' UTILIZA UN VBSCRIPT QUE SE CREA, SE EJECUTA '
' Y ESPERA A LA FINALIZACION DE LA APLICACION '
' PARA VOLVERLE A EJECUTAR                    '
'---------------------------------------------'
'
Dim strL_Fichero_01 As String
'
Dim objL_Script_01 As Object
Dim objL_Create_Script_01 As Object
'
strL_Fichero_01 = App.path & "\restart_api_" & Format(Now(), "YYYYMMDDHhNnSs") & ".vbs"
'
If (Dir(strL_Fichero_01)  "") Then Kill (strL_Fichero_01)
'
Set objL_Script_01 = CreateObject("Scripting.FileSystemObject")
'
Set objL_Create_Script_01 = objL_Script_01.CreateTextFile(strL_Fichero_01, False)
'
With objL_Create_Script_01
.WriteLine "Option Explicit"
.WriteLine "'-------------------------------------'"
.WriteLine "' VBSCRIPT QUE REINICIA LA APLICACION '"
.WriteLine "' '" & strP_Aplicacion_01 & "'        '"
.WriteLine "' CUANDO ESTA SE CIERRA               '"
.WriteLine "'-------------------------------------'"
.WriteLine "'"
.WriteLine "Const strComputer = ""."" "
.WriteLine "'"
.WriteLine "Dim objWMIService"
.WriteLine "Dim colProcessList"
.WriteLine "Dim bolL_Proceso_Terminado"
.WriteLine "Dim objProcess"
.WriteLine "Dim WshShell"
.WriteLine "Dim oExec"
.WriteLine "'"
.WriteLine "Set objWMIService = GetObject(""winmgmts:"" & ""{impersonationLevel=impersonate}!\\"" & strComputer & ""\root\cimv2"")"
.WriteLine "'"
.WriteLine "Do"
.WriteLine "    Set colProcessList = objWMIService.ExecQuery(""SELECT * FROM Win32_Process WHERE Name = '" & strP_Proceso_01 & "'"")"
.WriteLine "    bolL_Proceso_Terminado = true"
.WriteLine "    '"
.WriteLine "    For Each objProcess in colProcessList"
If (bolP_Control_ID_01 = False) Then
.WriteLine "        WScript.Echo ""¡ENCONTRADO EL PROCESO '" & strP_Proceso_01 & "' !"""
.WriteLine "        bolL_Proceso_Terminado = False"
''
Else
.WriteLine "        If (objProcess.ProcessId = " & lonP_ID_Proceso_01 & ") Then"
.WriteLine "            WScript.Echo ""¡ENCONTRADO EL PROCESO '" & strP_Proceso_01 & "' ID:" & lonP_ID_Proceso_01 & " !"""
.WriteLine "            bolL_Proceso_Terminado = False"
.WriteLine "            ''"
.WriteLine "        End If"
''
End If
.WriteLine "        '"
.WriteLine "        WScript.Sleep 1000 ' NO SATUREMOS EL SISTEMA"
.WriteLine "        ''"
.WriteLine "    Next"
.WriteLine "    ''"
.WriteLine "Loop While bolL_Proceso_Terminado = False"
.WriteLine "'"
.WriteLine "' EL PROCESO O TERMINO O NO EXISISTIA"
.WriteLine "' ENTONCES LO VOLVEMOS A EJECUTAR"
.WriteLine "'"
.WriteLine "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
.WriteLine "Set oExec = WshShell.Exec(""" & strP_Aplicacion_01 & """)"
.WriteLine "WScript.Echo ""¡ARRANQUE DE LA APLICACION '" & strP_Aplicacion_01 & "' FINALIZADO!"""
'
.Close
''
End With
'
Set objL_Create_Script_01 = Nothing
Set objL_Script_01 = Nothing
'
Call Log("MENSAJE_Reinicio_Aplicacion_01_01 - REINICIO APLICACION: '" & strP_Aplicacion_01 & "' ID:" & lonP_ID_Proceso_01)
'
Shell "cscript """ & strL_Fichero_01 & """"
''
End Sub

Como veis es un “metodo” bastante sencillo.
Al procedimiento “Reinicio_Aplicacion_01” se le pasan los parametros de:
Nombre del Proceso (no confundir con la aplicacion)
Ejecutable de la aplicacion (La aplicacion, llamada de la aplicacion con argumentos incluidos)
Y si lo deseamos (yo aqui lo hago) pasamos el ID del proceso.
Nos podemos encontrar con que la aplicacion se esta ejecutando en varios procesos, de esta manera podemos diferenciar que proceso hay que eliminar.

El procedimiento, con estos argumento crea un fichero .VBS (vbscript) personalizado a nuestra aplicacion.
Posteriormente el procedimiento lanza el script.
El script espera hasta que la aplicacion se cierre.
La aplicacion se cierran con el “End”.
Y el script relanza la aplicacion.

En esta entrada el scrpit espera a que la aplicacion se cierre. Aunuque, como puedes intuir, se puede modificar el script para que haga mas cosas, como por ejemplo cerrar la aplicacion (matando el proceso) y relanzarla. Pero eso posiblemente sea tema para otra entrada.

Tengo que insistir que esto es simplemnte una ayuda para tener la aplciacion en ejecucion y que siempre tendremos que trabajar en la depuracion de errores hasta que ya no necesitemos la llama a la funcion de reinicio. Ten encuenta que dependiendo de la complejedad de la aplicacion fporzar el cerrado puede causar serios problemas (conexiones a bases de datos, ficheros corruptos o mal cerrados, etc…)

Por otro lado, comentar que esto lo estoy aplicando desde hace unos meses y el resultado ha sido excelente.
Sinceramente me sorprendió (mas bien sigo sorprendido), llevo semanas sin tener que hacer nada en las aplicaciones (como el anuncio de las pilas… y duran… y duran… y duran…)

Nota:
GetCurrentProcessId
Función API que devuelve el ID del proceso de la propia aplicacion.
Public Declare Function GetCurrentProcessId Lib “Kernel32” () As Long

 

Anuncios

Acerca de Robert Ale
Soy yo un simple tipo que quiere estar en la red... aunque no se si lo conseguiré algún día...

2 Responses to VB6 – Aplicación visual que se reinicia sola

  1. Mariana says:

    Gracias por el aporte! Estoy trabajando en un programa con vb6 para guardar datos de un horno industrial. El problema que estuve teniendo es que guarda los datos durante un buen tiempo (8hs) y se cierra antes de finalizar el tiempo total (12hs).
    Mi problema es que necesito que el programa guarde hasta el ultimo dato y el final genere un PDF, pero se cierra antes de tiempo, sin tirar ninguna ventana de error. =(

    Lei el codigo y lo voy a aplicar a mi programa. Lo que no comprendo es que significa Call Log, o sea llama a un procedimiento Log Y QUE HACE? =/

    Espero que me puedas ayudar pronto!

    • Robert Ale says:

      Hola! De nada!

      Vamos por partes:

      Lo del “Call Log” es simplemente la llamada a un procedimiento llamado “Log” que solo se dedica a escribir un texto en un fichero de texto a modo de log o historico.
      Es muy simple y solo añade lo que yo quiere y en los puntos o momentos que yo quiero.

      Ayuda bastante para tener un historico de las operaciones que van realizando sin muchas complicaciones.
      Si te lo “curras”, puede ayudarte a anotar errores, etc…

      En cuanto al tema clave:
      Te entiendo que la aplicación, en un cierto momento, se cierra sin explicación.
      Lo primero y lo mas importante seria localizar porque se cierra, puede ser de utilizad utilizar el procedimiento “Log”, pero solo sirve de algo si lo colocas en los puntos clave que te puedan ayudar a anotar los fallos que surjan.

      Evita usar los “On Error Resume Next” o por lo menos reducirlos. Camuflan muchos errores graves (a parte que enlentece la aplicación).

      Y para cuando no se puede hacer mas, la solución que veo es tener otra aplicación a parte, independiente, que monitorice cada cierto tiempo (con un timer) si la aplicación existe y/o esta congelada.
      Así si detecta que esta cerrada la aplicación del horno, pues la arranca.
      Si la encuentra congelada, la cierrar y la vuelve a arrancar.

      Es una aplicación bastante sencilla. Y en sistemas similares lo he utilizando.

      Saludos.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: