Saltar al contenido

Cómo esperar a que finalice un proceso de shell antes de ejecutar más código en VB6

Luego de consultar con especialistas en este tema, programadores de diversas ramas y maestros dimos con la respuesta a la interrogande y la compartimos en este post.

Solución:

La salsa secreta necesaria para hacer esto es la WaitForSingleObject función, que bloquea la ejecución del proceso de su aplicación hasta que el proceso especificado se complete (o se agote el tiempo de espera). Es parte de la API de Windows, se puede llamar fácilmente desde una aplicación VB 6 después de agregar la declaración adecuada a su código.

Esa declaración se vería así:

Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle _
                            As Long, ByVal dwMilliseconds As Long) As Long

Se necesitan dos parámetros: un identificador del proceso que desea esperar y el intervalo de tiempo de espera (en milisegundos) que indica la cantidad máxima de tiempo que desea esperar. Si no especifica un intervalo de tiempo de espera (un valor de cero), la función no espera y regresa inmediatamente. Si especifica un intervalo de tiempo de espera infinito, la función devuelve solamente cuando el proceso indica que se ha completado.

Armado con ese conocimiento, la única tarea que queda es descubrir cómo manejar el proceso que inició. Eso resulta ser bastante simple y se puede lograr de diferentes maneras:

  1. Una posibilidad (y la forma en que lo haría) es usando el ShellExecuteEx función, también de la API de Windows, como un reemplazo directo de la Shell función que está integrada en VB 6. Esta versión es mucho más versátil y poderosa, pero con la misma facilidad se llama usando la declaración apropiada.

    Devuelve un identificador al proceso que crea. Todo lo que tienes que hacer es pasar esa manija al WaitForSingleObject funcionar como el hHandle parámetro, y ya está en el negocio. La ejecución de su aplicación se bloqueará (suspenderá) hasta que finalice el proceso que ha llamado.

  2. Otra posibilidad es utilizar el CreateProcess función (una vez más, de la API de Windows). Esta función crea un nuevo proceso y su hilo principal en el mismo contexto de seguridad que el proceso de llamada (es decir, su aplicación VB 6).

    Microsoft ha publicado un artículo de la base de conocimientos que detalla este enfoque que incluso proporciona una implementación de muestra completa. Puede encontrar ese artículo aquí: Cómo utilizar una aplicación de 32 bits para determinar cuándo finaliza un proceso Shelt.

  3. Finalmente, quizás el enfoque más simple hasta ahora es aprovechar el hecho de que el Shell El valor de retorno de la función es un ID de tarea de aplicación. Este es un número único que identifica el programa que inició y se puede pasar al OpenProcess función para obtener un identificador de proceso que se puede pasar al WaitForSingleObject función.

    Sin embargo, la simplicidad de este enfoque lo hace tienen un costo. Una desventaja muy significativa es que hará que su aplicación VB 6 deje de responder por completo. Debido a que no procesará los mensajes de Windows, no responderá a la interacción del usuario ni tampoco volverá a dibujar la pantalla.

    La buena gente de VBnet ha puesto a disposición un código de muestra completo en el siguiente artículo: WaitForSingleObject: Determine cuándo ha finalizado una aplicación Shelt.
    Me encantaría poder reproducir el código aquí para ayudar a evitar la pudrición del enlace (VB 6 está llegando allí en años; no hay garantía de que estos recursos estén disponibles para siempre), pero aparece la licencia de distribución en el código mismo. para prohibirlo explícitamente.

No hay necesidad de recurrir al esfuerzo adicional de llamar a CreateProcess (), etc. Esto duplica más o menos el antiguo código de Randy Birch, aunque no se basó en su ejemplo. Hay tantas formas de despellejar a un gato.

Aquí tenemos una función preempaquetada para un uso práctico, que también devuelve el código de salida. Déjelo caer en un static (.BAS) o inclúyalo en línea en un formulario o clase.

Option Explicit

Private Const INFINITE = &HFFFFFFFF&
Private Const SYNCHRONIZE = &H100000
Private Const PROCESS_QUERY_INFORMATION = &H400&

Private Declare Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As Long) As Long

Private Declare Function GetExitCodeProcess Lib "kernel32" ( _
    ByVal hProcess As Long, _
    lpExitCode As Long) As Long

Private Declare Function OpenProcess Lib "kernel32" ( _
    ByVal dwDesiredAccess As Long, _
    ByVal bInheritHandle As Long, _
    ByVal dwProcessId As Long) As Long

Private Declare Function WaitForSingleObject Lib "kernel32" ( _
    ByVal hHandle As Long, _
    ByVal dwMilliseconds As Long) As Long

Public Function ShellSync( _
    ByVal PathName As String, _
    ByVal WindowStyle As VbAppWinStyle) As Long
    'Shell and wait.  Return exit code result, raise an
    'exception on any error.
    Dim lngPid As Long
    Dim lngHandle As Long
    Dim lngExitCode As Long

    lngPid = Shell(PathName, WindowStyle)
    If lngPid <> 0 Then
        lngHandle = OpenProcess(SYNCHRONIZE _
                             Or PROCESS_QUERY_INFORMATION, 0, lngPid)
        If lngHandle <> 0 Then
            WaitForSingleObject lngHandle, INFINITE
            If GetExitCodeProcess(lngHandle, lngExitCode) <> 0 Then
                ShellSync = lngExitCode
                CloseHandle lngHandle
            Else
                CloseHandle lngHandle
                Err.Raise &H8004AA00, "ShellSync", _
                          "Failed to retrieve exit code, error " _
                        & CStr(Err.LastDllError)
            End If
        Else
            Err.Raise &H8004AA01, "ShellSync", _
                      "Failed to open child process"
        End If
    Else
        Err.Raise &H8004AA02, "ShellSync", _
                  "Failed to Shell child process"
    End If
End Function

Sé que es un hilo antiguo, pero …

¿Qué tal usar el método Run de Windows Script Host? Tiene un parámetro bWaitOnReturn.

object.Run (strCommand, [intWindowStyle], [bWaitOnReturn])

Set oShell = CreateObject("WSCript.shell")
oShell.run "cmd /C " & App.Path & sCommand, 0, True

intWindowStyle = 0, por lo que cmd estará oculto

Te mostramos reseñas y valoraciones

Recuerda que puedes comunicar esta reseña si si solucionó tu problema.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *