Make your application to wait for another process

To invoke an executable program from VB you can use Shell() function, but this function starts an executable program asynchronously and returns control to the procedure which called the function. The shelled program continues to run independently of your procedure until you close it.

There is an efficient but a bit complex way to make your procedure to wait until the shelled program ends. In this method you need to find out the process handle for the process shelled. Shell function won't return process handle, so to invoke the process you have to use 'CreateProcess' API instead. This CreateProcess will return process handle, using this handle 'WaitForSingleObject' is to be called to make the application to wait.

To test this code create a new project, insert a new form, draw a command button there and then paste the following code in 'general' section of the code window.

'Type declarations to pass to CreateProcess API
Private Type STARTUPINFO
    cb As Long
    lpReserved As String
    lpDesktop As String
    lpTitle As String
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Long
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type

Private Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessID As Long
    dwThreadID As Long
End Type

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

Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
   lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
   lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
   ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
   ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
   lpStartupInfo As STARTUPINFO, lpProcessInformation As _
   PROCESS_INFORMATION) As Long

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

Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Private Const SW_SHOWMINIMIZED = 2
Private Const STARTF_USESHOWWINDOW = &H1
Private Const SW_HIDE = 0
Private Const WAIT_ABANDONED = &H80
Private Const WAIT_OBJECT_0 = &H0
Private Const WAIT_TIMEOUT = &H102

'Create a procedure to execute the command
Public Sub ExecCmd(Cmdline As String)
    Dim proc As PROCESS_INFORMATION
    Dim start As STARTUPINFO
    Dim ReturnValue As Integer
    Dim lTimeOut As Long
    ' Initialize the STARTUPINFO structure:
    start.cb = Len(start)
    start.dwFlags = STARTF_USESHOWWINDOW
    start.wShowWindow = SW_HIDE
 
    ' Start the shelled application:
    ReturnValue = CreateProcessA(0&, Cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
 
    ' Set time out in milliseconds
    lTimeOut = 1000
    ' Wait for the shelled application to finish:
    Do
        ReturnValue = WaitForSingleObject(proc.hProcess, lTimeOut)
        DoEvents
    Loop Until ReturnValue <> WAIT_TIMEOUT
 
    ' Close the process handle
    ReturnValue = CloseHandle(proc.hProcess)
End Sub

Sub Command1_Click()
    ExecCmd "CALC.EXE"
    MsgBox "Process Finished"
End Sub

Now, execute the project and click the command button which will invoke calculator application and waits until it finishes. When the calculator is closed, it'll popup a message 'Process Finished'.

The button click event passes 'CALC.EXE' command line to ExecCmd procedure. ExecCmd will invoke calc.exe and get the handle, using the handle it will call 'WaitForSingleObject' API. This will be called until it returns 258 which means until the calculator is closed. So when the calculator is closed, it will come out from the loop and returns to click event, then 'process finished' message will be displayed.