411 REFERENCE · Wikibooks
Visual Basic/External Processes
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
It is often the case that a program already exists to do some job so
rather than rewrite the program in Visual Basic it is more efficient
to run the existing program from Visual Basic. This can work very
well for command line programs or scripts written in any language.
The general scheme is to create a string containing the command line,
call a function to run it and then wait for the command to complete.
There are many refinements that can be made including all sorts of
ways of sending data to the new process, reading results from it,
pausing it, setting its priority and so on.
The Shell Function
The simplest way of running an external program is to do something like this:
Shell "cmd /c dir %temp%"
Shell is a built in Visual Basic function that executes a command
line and returns a handle to that process. Shell takes an
optional argument that controls the window style of the new
process (maximized, normal, hidden, etc.).
Unfortunately running another program like this doesn't give you much
control over what is happening. In particular, there is no obvious
way to find out if the program has completed its work.
Luckily Shell actually does slightly more than launch the program.
It also returns the process id. This is the number displayed by
Task Managers and can be used to check the status of the process.
Unfortunately, as is so often the case with Visual Basic and Windows,
there is no built in function nor even a single API function that
can use it directly. Therefore, we must write a little more code. First, we
must declare a few API functions and constants:
Option Explicit
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, and 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
Dim ProcessID As Long
Dim hProcess As Long
Here is an example of a function that executes a command and waits for
it to complete:
Function ShellWait(CommandLine As String, _
TimeOut As Long, _
WindowState As VbAppWinStyle) As Boolean
Dim ProcessID As Long
Dim hProcess As Long
ProcessID = Shell(CommandLine,WindowState)
If ProcessID <> 0 Then
'non-zero (True) so Shell worked
' Get a process handle for the PID (Wait takes a handle)
hProcess = OpenProcess(SYNCHRONIZE, False, ProcessID)
If hProcess <> 0 Then
' Got process handle
' Wait until process finishes before going on
If WaitForSingleObject(hProcess, TimeOut) = WAIT_OBJECT_0 Then
ShellWait = True
Else
ShellWait = False
End If
Else
'Failed to get process handle.
'Perhaps the process terminated very quickly
'or it might not really have executed at all even though Windows
' started a process.
ShellWait = False
End If
Else
' PID zero (False) so Shell failed
ShellWait = False
End If
End Function
Call it like this:
If ShellWait("calc.exe") then
MsgBox "Success :-)"
Else
MsgBox "Failure :-("
End If
Exercises
Modify the ShellWait example function so that the caller can distinguish between failure to launch the process and timeout.
Suggest a reason why this might be a good idea.
Windows_API Object Oriented Programming