Waiting For A Shelled Program To End

Using the Shell method, in any version of Visual Basic, does not allow you to do this. You will need to use an API call and wait for the called program to end. We will discuss how to do this in 16-bit versions of Visual Basic and 32-bit versions.

Shelling From Visual Basic 16-bit

This code will work under any 16-bit version of Visual Basic. Waiting for a program to end is simple.

Declare

Declare Function GetModuleUsage Lib "Kernel" (ByVal _
     hModule As Integer) As Integer
Declare Function WinExec Lib "Kernel" (ByVal lpCmdLine _
     As String, ByVal nCmdShow As Integer) As Integer
Global Const SW_HIDE = 0
Global Const SW_SHOWNORMAL = 1
Global Const SW_NORMAL = 1
Global Const SW_SHOWMINIMIZED = 2
Global Const SW_SHOWMAXIMIZED = 3
Global Const SW_MAXIMIZE = 3
Global Const SW_SHOWNOACTIVATE = 4
Global Const SW_SHOW = 5
Global Const SW_MINIMIZE = 6
Global Const SW_SHOWMINNOACTIVE = 7
Global Const SW_SHOWNA = 8
Global Const SW_RESTORE = 9

Code

Sub DoShell (sShellString As String, iWinType As Integer)
     Dim iInstanceHandle As Integer
     Dim X As Integer
     iInstanceHandle = WinExec(sShellString, iWinType)
     Do While GetModuleUsage(iInstanceHandle) > 0
           X = DoEvents()
     Loop
End Sub

Usage

DoShell "C:\DOS\FORMAT.COM", SW_HIDE

This code uses the WinExec API call to start the program and return the instance handle. We simply sit in a DoEvents loop until GetModuleUsage (using the instance handle as the parameter) tells us that the instance of that called program is gone. You also can start the program in any of the SW_ modes you like, including hiding it!

Shelling From Visual Basic 32-bit

Shelling from a 16-bit version of Visual Basic is easy. Things are not so easy in 32-bit environments. With Windows 95 or Windows NT, every program runs its own distinct environment, which precludes polling if an instance of a program still exists. The following code will do the trick.

Declare

Declare Function OpenProcess Lib "kernel32" (ByVal _
     dwDesiredaccess&, ByVal bInherithandle&, _
     ByVal dwProcessid&) As Long
Declare Function GetExitCodeProcess Lib "kernel32" _
     (ByVal hProcess As Long, lpexitcode As Long) As Long
Const STILL_ACTIVE = &H103
Const PROCESS_QUERY_INFORMATION = &H400

Code

Sub ShellWait(sCommandLine As String)
     Dim hShell As Long
     Dim hProc As Long
     Dim lExit As Long
     hShell = Shell(sCommandLine, vbNormalFocus)
     hProc = OpenProcess(PROCESS_QUERY_INFORMATION, _
           False, hShell)
     Do
           GetExitCodeProcess hProc, lExit
           DoEvents
     Loop While lExit = STILL_ACTIVE
End Sub

Usage

ShellWait sCommandLine:="notepad.exe"

The trick with this code, as opposed to the method that the Microsoft KnowledgeBase has, is that it Shells the required program and then opens a process. It then waits for that process to finish. This way, if the program you Shelled calls other programs, you can continue on after the main (parent) program closes.

For example, if you Shelled the Internet Explorer, and the user started a download, you would not be able to continue until that download is done, even if the user closed Explorer. There might be times when you require this behavior (refer to article Q96844 of the Visual Basic KnowledgeBase). That code gives you an alternative.

 

This tip is reprinted from the VB Tips & Tricks Volume 1 book.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s