Best way to wait for a launched program to exit

What is the best way to use SHELL to launch a program and pause going to the next action in your launcher program until the program you launched has finished and exited?
LVL 4
shawnlehnerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

wertykCommented:
use this:
Shell("programname.exe", 1, true)

the third argument (true) makes it wait until its over before continuing

heres more info on the Shell command:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vafctshell.asp
0
RubynCommented:
werk, You are talking about VB.NET.
Try the code below:

Const SYNCHRONIZE = &H100000
Const INFINITE = &HFFFF
 'Wait forever
Const WAIT_OBJECT_0 = 0
 'The state of the specified object is signaled
Const WAIT_TIMEOUT = &H102
 'The time-out interval elapsed & the object’s state
'is nonsignaled.

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

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


Private Function ShellWait(txtApp As String, Optional Wait As Long = INFINITE) As Boolean
Dim lPid As Long, lHnd As Long, lRet As Long

If Trim$(txtApp) = "" Then Exit Function

Let lPid = Shell(txtApp, vbNormalFocus)
If lPid <> 0 Then
        'Get a handle to the shelled process.
        Let lHnd = OpenProcess(SYNCHRONIZE, 0, lPid)
        'If successful, wait for the application to end and close the handle.
        If lHnd <> 0 Then
                lRet = WaitForSingleObject(lHnd, Wait)
                Call CloseHandle(lHnd)
                Let ShellWait = True
        End If
End If
End Function

Private Sub Command1_Click()
Call ShellWait("notepad.exe")
End Sub
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
A variation of Rubyn's post which keeps the app from freezing up while it is waiting for the Shelled app to exit:

Option Explicit

Private Const SYNCHRONIZE = &H100000
Private Const WAIT_TIMEOUT = &H102

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
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Sub Command1_Click()
    ShellAndWait "calc.exe"
    MsgBox "Done"
End Sub

Private Sub ShellAndWait(ByVal cmd As String)
    Dim lPid As Long
    Dim lHnd As Long

    lPid = Shell(cmd, vbNormalFocus)
    If lPid <> 0 Then
        lHnd = OpenProcess(SYNCHRONIZE, 0, lPid)
        If lHnd <> 0 Then
            Do
                DoEvents ' keep application responsive
                Sleep 50 ' keep CPU usage from ramping to 100%
            Loop Until WaitForSingleObject(lHnd, 0) <> WAIT_TIMEOUT
            CloseHandle (lHnd)
        End If
    End If
End Sub
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

Mike TomlinsonMiddle School Assistant TeacherCommented:
A different way to do it.  Also gives you back the exit code:

Option Explicit

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Function ShellAndWait(ByVal fileName As String) As Long
    If Dir(fileName) <> "" Then
        Dim WShell As Object
        Dim oProcess As Object
       
        Set WShell = CreateObject("WScript.Shell")
        Set oProcess = WShell.Exec(fileName)

        Do While oProcess.Status = 0
            Sleep 50 ' keep CPU usage from ramping to 100%
            DoEvents ' keep app responsive
        Loop

        ShellAndWait = oProcess.ExitCode

        Set WShell = Nothing
        Set oProcess = Nothing
    Else
        MsgBox fileName, vbCritical, "File Not Found"
        ShellAndWait = 1 ' a non-zero value usually means something went wrong
    End If
End Function
0
RubynCommented:
Beware since using DoEvents, Timer Controls will still trigger their methods.
0
Robberbaron (robr)Commented:
my personal favorite as it appears as an extension to the std vb shell command... wish i could say i designed all the original.


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

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

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

Private Const PROCESS_QUERY_INFORMATION = &H400
Private Const STATUS_PENDING = &H103&
'---
Function ShellSync(action As String, windowstyle As Integer) As Integer
''''from VB5 help''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'  The standard Shell function runs other programs asynchronously.
'  This means that a program started with Shell might not finish
'  executing before the statements following the Shell function are executed.
'------------
'  ShellSync start the program asynchronously but waits for completion
'----------------------------------------------------------
' updated by R.Gray    June 2003

    Dim hProcess As Long
    Dim ProcessId As Long
    Dim exitCode As Long

    ProcessId = Shell(action, windowstyle)
    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, ProcessId)

    Do
        Call GetExitCodeProcess(hProcess, exitCode)
        DoEvents: DoEvents
    Loop While exitCode = STATUS_PENDING
    Call CloseHandle(hProcess)

    'MsgBox "The shelled process " & cmdline & " has ended." or check ProcessId <> 0
    ShellSync = ProcessId
End Function

'--end block--'

what the difference between GetExitCodeProcess & WaitForSingleObject is i dont know....

Rubyn's tip on timers is interesting as well...
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
jcoutureCommented:
Something to keep in mind... I've found that add-on apps like "Window Blinds" can mess around with process ids.  That means if the method you are using relies on a process id to track if the shelled application is still running then you may get false results if something like "Window Blinds" is loaded.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.