Problema con función Shell en VB.NET 2010

Iniciado por bybaal, 21 Diciembre 2015, 22:16 PM

0 Miembros y 1 Visitante están viendo este tema.

bybaal

Necesito ejecutar una aplicación con la función shell, utilizando el parámetro Wait = True, hacer esto mientras un timer cambia constantemente la propiedad Text del Formulario. El problema es que mientras se espera a que termine dicha aplicación, el timer deja de actualizar la propiedad Text del Formulario.

Aquí les dejo el código de ejemplo para ver si alguien puede ayudarme

Código (vbnet) [Seleccionar]
Public Class Form1
   Dim WithEvents tmrEfect As New Timer With {.Enabled = True, .Interval = 1000}

   Private Sub tmrEfect_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmrEfect.Tick
       Me.Text = Now
   End Sub

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
       Shell("calc.exe", AppWinStyle.NormalFocus, True)
       MsgBox("cerrada la aplicación")
   End Sub
End Class

Eleкtro

#1
Buenas!

1. Por favor, postea las preguntas de Vb.Net en la sección correcta. (hay muchos posts movidos que deberían haberte dado ya una pista...)

2. Debes utilizar las etiquetas Geshi para insertar código, lee las reglas del foro de programación general, gracias.




Es normal lo que te ocurre, ya que cuando activas el parámetro "wait", el hilo propietario desde el que has realizado la llamada se detiene por tiempo indefinido hasta que el proceso finalize su ejecución.

Para solucionar el problema que has descrito se me ocurren al menos dos maneras distintas,
pero antes de nada por cuestiones de productividad, rendimiento, y adquisición de buenas costumbres, te aconsejo parar ahora mismo de utilizar la función Microsoft.VisualBasic.Shell y cualquier otro miembro de ese espacio de nombres; reemplazalo por el uso de la Class System.Diagnostics.Process.

1. Puedes iniciar el proceso de la siguiente manera:
Código (vbnet) [Seleccionar]
Threading.Tasks.Task.Factory.StartNew(
   Sub()
       Using p As New Process
           p.StartInfo.FileName = "calc.exe"
           p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
           p.Start()
           p.WaitForExit()
       End Using

       MessageBox.Show("cerrada la aplicación", "Calc.exe",
                       MessageBoxButtons.OK, MessageBoxIcon.Information)

   End Sub, TaskCreationOptions.None)


Nota:
En otras circunstancias tal vez podrias preferir utilizar el flag TaskCreationOptions.LongRunning para forzar la creación de un nuevo hilo en el grupo de hilos por defecto, ganando así más velocidad de inicialización de la tarea al no esperar en la cola de hilos.

2. O puedes suscribirte al evento Process.Exited:
Código (vbnet) [Seleccionar]
Friend WithEvents Calc As Process

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

   Calc = New Process
   Calc.EnableRaisingEvents = True
   Calc.StartInfo.FileName = "calc.exe"
   Calc.StartInfo.WindowStyle = ProcessWindowStyle.Normal
   Calc.Start()

End Sub

Private Sub Calc_Exited() Handles Calc.Exited

   MessageBox.Show("cerrada la aplicación", "Calc.exe",
                           MessageBoxButtons.OK, MessageBoxIcon.Information)

   ' Seguir con la lógica aquí...

End Sub


Saludos








bybaal

Muchas Gracias, Muy buena la ayuda una explicación de libro.

Cita de: Eleкtro en 22 Diciembre 2015, 02:05 AM
Buenas!

1. Por favor, postea las preguntas de Vb.Net en la sección correcta. (hay muchos posts movidos que deberían haberte dado ya una pista...)

2. Debes utilizar las etiquetas Geshi para insertar código, lee las reglas del foro de programación general, gracias.




Es normal lo que te ocurre, ya que cuando activas el parámetro "wait", el hilo propietario desde el que has realizado la llamada se detiene por tiempo indefinido hasta que el proceso finalize su ejecución.

Para solucionar el problema que has descrito se me ocurren al menos dos maneras distintas,
pero antes de nada por cuestiones de productividad, rendimiento, y adquisición de buenas costumbres, te aconsejo parar ahora mismo de utilizar la función Microsoft.VisualBasic.Shell y cualquier otro miembro de ese espacio de nombres; reemplazalo por el uso de la Class System.Diagnostics.Process.

1. Puedes iniciar el proceso de la siguiente manera:
Código (vbnet) [Seleccionar]
Threading.Tasks.Task.Factory.StartNew(
   Sub()
       Using p As New Process
           p.StartInfo.FileName = "calc.exe"
           p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
           p.Start()
           p.WaitForExit()
       End Using

       MessageBox.Show("cerrada la aplicación", "Calc.exe",
                       MessageBoxButtons.OK, MessageBoxIcon.Information)

   End Sub, TaskCreationOptions.None)


Nota:
En otras circunstancias tal vez podrias preferir utilizar el flag TaskCreationOptions.LongRunning para forzar la creación de un nuevo hilo en el grupo de hilos por defecto, ganando así más velocidad de inicialización de la tarea al no esperar en la cola de hilos.

2. O puedes suscribirte al evento Process.Exited:
Código (vbnet) [Seleccionar]
Friend WithEvents Calc As Process

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

   Calc = New Process
   Calc.EnableRaisingEvents = True
   Calc.StartInfo.FileName = "calc.exe"
   Calc.StartInfo.WindowStyle = ProcessWindowStyle.Normal
   Calc.Start()

End Sub

Private Sub Calc_Exited() Handles Calc.Exited

   MessageBox.Show("cerrada la aplicación", "Calc.exe",
                           MessageBoxButtons.OK, MessageBoxIcon.Information)

   ' Seguir con la lógica aquí...

End Sub


Saludos

bybaal

#3
Siento tener que revivir este tema, pero la verdad es que a pesar de que el código funciona perfectamente, no he podido adaptarlo a lo que necesito, discúlpenme.

Anteriormente en VB6 yo usaba una función para ejecutar aplicaciones y esperar a que estas se cerrasen para continuar ejecutando el código de mi programa, sin que esto detuviera los timers que yo usaba en mi proyecto.

Ahora en VB.NET como adapto este código para poder usarlo varías veces seguidas dentro de un mismo evento.

Ejemplo:

Código (vbnet) [Seleccionar]
Imports System.Threading.Tasks

Public Class Form1

   Dim WithEvents tmrEfect As New Timer With {.Enabled = True, .Interval = 1000}

   Private Sub tmrEfect_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmrEfect.Tick
       Me.Text = Now
   End Sub

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
       RunWait("calc.exe", ProcessWindowStyle.Normal)
       'Aquí necesito poner varias líneas de código de tienen que ejecutarse al cerrarce la calculadora
       MsgBox("aplicación calc.exe cerrada")

       RunWait("mspaint.exe", ProcessWindowStyle.Normal)
       'Aquí necesito poner varias líneas de código de tienen que ejecutarse al cerrarce el paint
       MsgBox("aplicación mspaint.exe cerrada")
   End Sub

   Private Sub RunWait(ByVal FullPathFile As String, ByVal WindowStyle As ProcessWindowStyle)
       Threading.Tasks.Task.Factory.StartNew(
           Sub()
               Using p As New Process
                   p.StartInfo.FileName = FullPathFile
                   p.StartInfo.WindowStyle = WindowStyle
                   p.Start()
                   p.WaitForExit()
               End Using
           End Sub, TaskCreationOptions.None)
   End Sub
End Class

Eleкtro

#4
Cita de: Eleкtro en 22 Diciembre 2015, 02:05 AM2. Debes utilizar las etiquetas Geshi para insertar código, lee las reglas del foro de programación general, gracias.

Si el próximo mensaje con código incumple la regla que ya conoces, será eliminado :-\ lo siento.




Puedes "concatenar" la tarea con otra tarea de continuación asíncrona, utilizando la función ContinueWith
Código (vbnet) [Seleccionar]
Task.Factory.StartNew(
   Sub()
       Using p As New Process
           p.StartInfo.FileName = "calc.exe"
           p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
           p.Start()
           p.WaitForExit()
       End Using
   End Sub, TaskCreationOptions.None).ContinueWith(
   Sub()
       MessageBox.Show("calc.exe has exited.")
   End Sub)


O simplemente esperar a que finalice la tarea:
Código (vbnet) [Seleccionar]
Private Function RunWait(ByVal fullPathFile As String, ByVal windowStyle As ProcessWindowStyle) As Task

   Dim processTask As Action =
       Sub()
           Using p As New Process
               p.StartInfo.FileName = fullPathFile
               p.StartInfo.WindowStyle = windowStyle
               p.Start()
               p.WaitForExit()
           End Using
       End Sub

   Return New Task(processTask)

End Function

...
Código (vbnet) [Seleccionar]
Sub Test()... Handles...

   Dim t As Task = RunWait("calc.exe", ProcessWindowStyle.Normal)
   t.Start()
   t.Wait()

   MessageBox.Show("calc.exe has exited.")

End Sub


PD: La primera solución tiene la ventaja de no bloquear el thread propietario de la llamada a esa Task.

Saludos








bybaal

Eso es exactamente lo que necesito, sólo queda un pequeño detalle, que para mi es el más problemático y para que puedan ayudarme, voy a tratar de explicar mejor lo que quiero hacer.

El programa ejecuta varias aplicaciones a lo largo de un procedimiento y es espera a que terminen cada una de ellas para poder continuar con el resto del código. Hasta aquí vamos bien con lo logrado hasta ahora, el problema es que mientras se espera a que las aplicaciones terminen un timer tiene que ir haciendo cambios en el formulario, como uso en el ejemplo cambiar el texto del titulo del form, pero puede ser cualquier otra cosa para indicar que el programa está funcionando, como pudiera ser también cambiar la imagen de un picturebox para mostrar una secuencia de imágenes de un reloj de arena.

Por ejemplo:

Código (vbnet) [Seleccionar]

Imports System.Threading.Tasks

Public Class Form1
    Dim WithEvents tmrEfect As New Timer With {.Enabled = True, .Interval = 1000}

    Private Function RunWait(ByVal fullPathFile As String, ByVal windowStyle As ProcessWindowStyle) As Task

        Dim processTask As Action =
            Sub()
                Using p As New Process
                    p.StartInfo.FileName = fullPathFile
                    p.StartInfo.WindowStyle = windowStyle
                    p.Start()
                    p.WaitForExit()
                End Using
            End Sub

        Return New Task(processTask)

    End Function

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim t As Task = RunWait("calc.exe", ProcessWindowStyle.Normal)
        t.Start()
        t.Wait()
        MessageBox.Show("calc.exe has exited.")

        t = RunWait("mspaint.exe", ProcessWindowStyle.Normal)
        t.Start()
        t.Wait()
        MessageBox.Show("mspaint.exe has exited.")
    End Sub

    Private Sub tmrEfect_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmrEfect.Tick
        Me.Text &= "."
        If Len(Me.Text) = 10 Then Me.Text = "Espere"
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Text = "Espere"
    End Sub
End Class


PD: Disculpen por la demora en responder.

Eleкtro

Código (vbnet) [Seleccionar]
Private ReadOnly title As String = "Espere"
Friend WithEvents TimerEfect As Global.System.Windows.Forms.Timer

Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load

    Me.Text = Me.title
    Me.TimerEfect = New Global.System.Windows.Forms.Timer With {.Enabled = True, .Interval = 1000}

End Sub

Private Sub TimerEfect_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles TimerEfect.Tick

    If Me.Text.EndsWith("...") Then
        Me.Text = Me.title

    Else
        Me.Text &= "."

    End If

End Sub

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click

    Task.Factory.StartNew(
        Sub()
            Dim t As Task = RunWait("calc.exe", ProcessWindowStyle.Normal)
            t.Start()
            t.Wait()
            MessageBox.Show("calc.exe has exited.")

            t = RunWait("mspaint.exe", ProcessWindowStyle.Normal)
            t.Start()
            t.Wait()
            MessageBox.Show("mspaint.exe has exited.")
        End Sub)

End Sub

Private Function RunWait(ByVal fullPathFile As String, ByVal windowStyle As ProcessWindowStyle) As Task

    Dim processTask As Action =
        Sub()
            Using p As New Process
                p.StartInfo.FileName = fullPathFile
                p.StartInfo.WindowStyle = windowStyle
                p.Start()
                p.WaitForExit()
            End Using
        End Sub

    Return New Task(processTask)

End Function


Saludos








bybaal

Ahora si quedó perfecto.

Muchas gracias por la ayuda y por aguantar tantas molestias

Cita de: Eleкtro en  2 Enero 2016, 22:13 PM
Código (vbnet) [Seleccionar]
Private ReadOnly title As String = "Espere"
Friend WithEvents TimerEfect As Global.System.Windows.Forms.Timer

Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load

    Me.Text = Me.title
    Me.TimerEfect = New Global.System.Windows.Forms.Timer With {.Enabled = True, .Interval = 1000}

End Sub

Private Sub TimerEfect_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles TimerEfect.Tick

    If Me.Text.EndsWith("...") Then
        Me.Text = Me.title

    Else
        Me.Text &= "."

    End If

End Sub

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click

    Task.Factory.StartNew(
        Sub()
            Dim t As Task = RunWait("calc.exe", ProcessWindowStyle.Normal)
            t.Start()
            t.Wait()
            MessageBox.Show("calc.exe has exited.")

            t = RunWait("mspaint.exe", ProcessWindowStyle.Normal)
            t.Start()
            t.Wait()
            MessageBox.Show("mspaint.exe has exited.")
        End Sub)

End Sub

Private Function RunWait(ByVal fullPathFile As String, ByVal windowStyle As ProcessWindowStyle) As Task

    Dim processTask As Action =
        Sub()
            Using p As New Process
                p.StartInfo.FileName = fullPathFile
                p.StartInfo.WindowStyle = windowStyle
                p.Start()
                p.WaitForExit()
            End Using
        End Sub

    Return New Task(processTask)

End Function


Saludos