If you've wondered how to write code that waits for a process to finish before terminating it, here's the answer.
I have seen a number of discussions regarding the need for a VB script that waits for a process to finish. The script in this hack does this and more: it waits for a process to finish and optionally terminates the process if it has not finished within a specified amount of time.
This code is a modified form of what I use to control my software deployments, and it has two purposes. First, the code is designed to be certain that the deployment script waits until the initiated software setup executable is fully finished before proceeding. Even though the majority of recent software releases do not require this functionality when being deployed, it is still required for some legacy installations. Second, the code can perform a forceful termination of an application if this functionality is required.
This script accepts three arguments: the name of the executable to wait for or terminate, the amount of time to wait before terminating the specified executable, and (optionally) a switch specifying that the script should run silently. Note that the script uses Windows Management Instrumentation (WMI) for the process-management tasks, so make sure you're running the latest WMI version on your machine.
The script consists of several sections, which are described inline in the following sections.
First, command-line switches are read in the main body area:
Option Explicit ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ' File: vbsWaitForProcess.vbs ' Updated: Nov 2002 ' Version: 1.0 ' Author: Dan Thomson, myITforum.com columnist ' I can be contacted at dethomson@hotmail.com ' ' Usage: The command processor version must be run using cscript ' cscript vbsWaitForProcess.vbs notepad.exe 60 S ' or ' The IE and Popup versions can be run with cscript or wscript ' wscript vbsWaitForProcess.vbs notepad.exe -1 ' ' Input: Name of executable (ex: notepad.exe) ' Time to wait in seconds before terminating the executable ' -1 waits indefinitely for the process to finish ' 0 terminates the process imediately ' Any value > 0 will cause the script to wait the specified ' amount of time in seconds before terminating the process ' Silent mode (S) ' ' Notes: ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' On Error Resume Next 'Define some variables Dim strProcess Dim intWaitTime Dim strSilent 'Get the command line arguments strProcess = Wscript.Arguments.Item(0) intWaitTime = CInt(Wscript.Arguments.Item(1)) strSilent = Wscript.Arguments.Item(2) Call WaitForProcess (strProcess, intWaitTime, strSilent)
Next, the ProcessIsRunning function determines if a process is running:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ' Function: ProcessIsRunning ' ' Purpose: Determine if a process is running ' ' Input: Name of process ' ' Output: True or False depending on if the process is running ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Function ProcessIsRunning( strProcess ) Dim colProcessList Set colProcessList = Getobject("Winmgmts:").Execquery _ ("Select * from Win32_Process Where Name ='" & strProcess & "'") If colProcessList.Count > 0 Then ProcessIsRunning = True Else ProcessIsRunning = False End If Set colProcessList = Nothing End Function
In the next section, the ProcessTerminate function terminates a process:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ' Function: TerminateProcess ' ' Purpose: Terminates a process ' ' Input: Name of process ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Function ProcessTerminate( strProcess ) Dim colProcessList, objProcess Set colProcessList = GetObject("Winmgmts:").ExecQuery _ ("Select * from Win32_Process Where Name ='" & strProcess & "'") For Each objProcess in colProcessList objProcess.Terminate( ) Next Set colProcessList = Nothing End Function
Finally, in the WaitForProcess subroutine, the user interface is set up, the script waits while the process is active, and the process termination is initiated. I created three versions of the subroutine in an effort to demonstrate a few methods for displaying status messages. For example, here's how to display these messages using the command console:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ' Sub: WaitForProcess ' ' Purpose: Waits for a process ' ' Input: Name of process ' Wait time in seconds before termination. ' -1 will cause the script to wait indefinitely ' 0 terminates the process imediately ' Any value > 0 will cause the script to wait the specified ' amount of time in seconds before terminating the process ' Display mode. ' Passing S will run the script silent and not show any prompts ' ' Output: On screen status ' ' Notes: The version echos user messages in the command window via StdOut ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Sub WaitForProcess( strProcess, intWaitTime, strMode ) If ProcessIsRunning(strProcess) Then Dim StdOut Dim w : w = 0 Dim strPrompt Dim intPause : intPause = 1 If UCase(strMode) <> "S" Then strPrompt = "Waiting for " & strProcess & " to finish." Set StdOut = WScript.StdOut StdOut.WriteLine "" StdOut.Write strPrompt End If 'Loop while the process is running Do While ProcessIsRunning(strProcess) 'Check to see if specified # of seconds have passed before terminating 'the process. If yes, then terminate the process If w >= intWaitTime AND intWaitTime >= 0 Then Call ProcessTerminate(strProcess) Exit Do End If 'If not running silent, post user messages If UCase(strMode) <> "S" Then _ StdOut.Write "." 'Increment the seconds counter w = w + intPause 'Pause Wscript.Sleep(intPause * 1000) Loop If UCase(strMode) <> "S" Then StdOut.WriteLine "" Set StdOut = Nothing End If End If End Sub
The result is shown in Figure 1-8.
Alternatively, here's some code for displaying status messages in Internet Explorer:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ' Sub: WaitForProcess ' ' Purpose: Waits for a process ' ' Input: Name of process ' Wait time in seconds before termination. ' -1 will cause the script to wait indefinitely ' 0 terminates the process imediately ' Any value > 0 will cause the script to wait the specified ' amount of time in seconds before terminating the process ' Display mode. ' Passing S will run the script silent and not show any prompts ' ' Output: On screen status ' ' Notes: This version uses Internet Explorer for user messages ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Sub WaitForProcess( strProcess, intWaitTime, strMode ) If ProcessIsRunning(strProcess) Then Dim objIntExplorer Dim c : c = 0 Dim w : w = 0 Dim strPrompt Dim intPause : intPause = 1 strPrompt = "Waiting for " & strProcess & " to finish." 'If not running silent, create reference to objIntExplorer 'This will be used for the user messages. Also set IE display attributes If UCase(strMode) <> "S" Then Set objIntExplorer = Wscript._ CreateObject("InternetExplorer.Application") With objIntExplorer .Navigate "about:blank" .ToolBar = 0 .Menubar = 0 ' no menu .StatusBar = 0 .Width=400 .Height = 80 .Left = 100 .Top = 100 .Document.Title = "WaitForProcess" End With 'Wait for IE to finish Do While (objIntExplorer.Busy) Wscript.Sleep 200 Loop 'Show IE objIntExplorer.Visible = 1 End If Do While ProcessIsRunning(strProcess) 'Check to see if specified # of seconds have passed before terminating 'the process. If yes, then terminate the process If w >= intWaitTime AND intWaitTime >= 0 Then Call ProcessTerminate(strProcess) Exit Do End If If UCase(strMode) <> "S" Then objIntExplorer.Document.Body.InnerHTML = strPrompt & String(c, ".") 'Increment the counter. 'Reset the counter indicator if it's > 25 because 'we don't want it taking up a lot of screen space. If c > 25 Then c = 1 Else c = c + 1 'Increment the seconds counter w = w + intPause End If 'Pause Wscript.Sleep(intPause * 1000) Loop objIntExplorer.Quit( ) ' close Internet Explorer Set objIntExplorer = Nothing ' release object reference End If End Sub
The resulting status message is shown in Figure 1-9.
Finally, here's code that uses the Popup method of Windows Scripting Host for displaying status messages:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ' Sub: WaitForProcess ' ' Purpose: Waits for a process ' ' Input: Name of process ' Wait time in seconds before termination. ' -1 will cause the script to wait indefinitely ' 0 terminates the process imediately ' Any value > 0 will cause the script to wait the specified ' ' amount of time in seconds before terminating the process ' Display mode. ' Passing S will run the script silent and not show any prompts ' ' Output: On screen status ' ' Notes: This version uses WshShell.Popup for user messages ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Sub WaitForProcess( strProcess, intWaitTime, strMode ) If ProcessIsRunning(strProcess) Then Dim objWshShell Dim c : c = 0 Dim w : w = 0 Dim strPrompt Dim intPopupTimer : intPopupTimer = 2 Dim intPause : intPause = 1 strPrompt = "Waiting for " & strProcess & " to finish." 'If not running silent, create reference to objWshShell 'This will be used for the user messages If UCase(strMode) <> "S" Then _ Set objWshShell = CreateObject("WScript.Shell") 'Loop while the process is running Do While ProcessIsRunning(strProcess) 'Check to see if specified # of seconds have passed before terminating 'the process. If yes, then terminate the process If w >= intWaitTime AND intWaitTime >= 0 Then Call ProcessTerminate(strProcess) Exit Do End If 'If not running silent, post user prompt If UCase(strMode) <> "S" Then objWshShell.Popup strPrompt & String(c, "."), intPopupTimer, _ "WaitForProcess", 64 'Increment the counter. 'Reset the counter indicator if it's > 25 because 'we don't want it taking up a lot of screen space. If c > 25 Then c = 1 Else c = c + 1 End If 'Increment the seconds counter w = w + intPause + intPopupTimer 'Pause Wscript.Sleep(intPause * 1000) Loop Set objWshShell = Nothing End If End Sub
The resulting dialog box is shown in Figure 1-10.
Note that if you are assembling a standalone script, it should contain sections 1, 2, 3, and one option from section 4. If you would rather incorporate this code into your existing script, you need only sections 2, 3, and one option from section 4. You'll also need to add the call statement that is at the end of the main routine section. All the code sections are self-contained, which makes them easy to import into existing scripts.
To use this hack, type the code into Notepad (with Word Wrap disabled) and save it with a .vbs extension as WaitForProcess.vbs. Or, if you don't want to tire your fingers out, download it from the O'Reilly web site instead.
Here are a few sample command-line examples. This will wait indefinitely until Notepad is closed:
cscript WaitForProcess.vbs notepad.exe -1
This will wait silently and indefinitely until Notepad is closed:
cscript WaitForProcess.vbs notepad.exe -1 S
And this will wait 10 seconds before Notepad is forcefully closed:
cscript WaitForProcess.vbs notepad.exe 10
?Dan Thomson