Waiting+for+an+external+program+to+terminate

//by Richard Russell, June 2006//

The ***RUN** command (or, equivalently, the **OSCLI "RUN"** statement) executes a specified external application and (by default) //waits for the application to terminate// before returning control to your BASIC program. This is often what is required: for example if the external application creates a file you probably don't want to continue execution of your BASIC program until the file has been created.

This is fine when ***RUN** does what you need, but on occasion you may need to use the additional facilities provided by the **ShellExecute** Windows API function. In particular **ShellExecute** allows you to specify the working directory used by the external application and to specify the //show state// of that application's window. For example it allows you to run the application in a //hidden// window, which may well be useful when you don't want the user to be troubled by the appearance of another window.

The syntax of the **ShellExecute** function when used for this purpose is as follows: code format="bb4w" SYS "ShellExecute", @hwnd%, 0, prog$, parm$, cdir$, show% code Here **prog$** is the name of the application you want to run (e.g. ), **parm$** contains the //command line// parameters for the application, if any (in the case of an editor like NOTEPAD that would typically be the name of the file to edit), **cdir$** is the working directory you want the application to use and **show%** is the window's show state (e.g. **0** to hide the window, **1** to show it normally).

So you might for example use a statement such as this: code format="bb4w" SYS "ShellExecute", @hwnd%, 0, "notepad.exe", "test.txt", "C:\", 1 code However the use of **ShellExecute** has one significant disadvantage compared to the use of ***RUN**: it does //not// wait until the external application has terminated. Control will be returned to your BASIC program immediately.

To overcome this disadvantage you can use the procedure listed below. This behaves like **ShellExecute** except that it does not return until the external application has terminated: code format="bb4w" DEF PROCexecuteandwait(prog$,parm$,cdir$,show%) LOCAL sei{}, res% DIM sei{cbSize%,fMask%,hwnd%,lpVerb%,lpFile%,lpParameters%,lpDirectory%,nShow%, \ \      hInstApp%,lpIDList%,lpClass%,hkeyClass%,dwHotKey%,hIcon%,hProcess%} prog$ += CHR$0 parm$ += CHR$0 cdir$ += CHR$0 sei.cbSize% = DIM(sei{}) sei.fMask% = 64 : REM SEE_MASK_NOCLOSEPROCESS sei.hwnd% = @hwnd% sei.lpFile% = !^prog$ sei.lpParameters% = !^parm$ sei.lpDirectory% = !^cdir$ sei.nShow% = show% SYS "ShellExecuteEx", sei{} IF sei.hProcess% = 0 THEN ENDPROC REPEAT SYS "WaitForSingleObject", sei.hProcess%, 100 TO res% UNTIL res%<>258 : REM WAIT_TIMEOUT SYS "CloseHandle", sei.hProcess% ENDPROC code So, by analogy with the example above, you might call this procedure as follows: code format="bb4w" PROCexecuteandwait("notepad.exe", "test.txt", "C:\", 1) code If you need to know the exit code (ERRORLEVEL) of the program then the above code can be converted to a function which returns this value: code format="bb4w" DEF FNexecuteandwait(prog$,parm$,cdir$,show%) LOCAL sei{}, res% DIM sei{cbSize%,fMask%,hwnd%,lpVerb%,lpFile%,lpParameters%,lpDirectory%,nShow%, \ \      hInstApp%,lpIDList%,lpClass%,hkeyClass%,dwHotKey%,hIcon%,hProcess%} prog$ += CHR$0 parm$ += CHR$0 cdir$ += CHR$0 sei.cbSize% = DIM(sei{}) sei.fMask% = 64 : REM SEE_MASK_NOCLOSEPROCESS sei.hwnd% = @hwnd% sei.lpFile% = !^prog$ sei.lpParameters% = !^parm$ sei.lpDirectory% = !^cdir$ sei.nShow% = show% SYS "ShellExecuteEx", sei{} IF sei.hProcess% = 0 THEN = 1067 REPEAT SYS "WaitForSingleObject", sei.hProcess%, 100 TO res% UNTIL res%<>258 : REM WAIT_TIMEOUT SYS "GetExitCodeProcess", sei.hProcess%, ^res% SYS "CloseHandle", sei.hProcess% = res% code You might call it as follows: code format="bb4w" exitcode% = FNexecuteandwait("notepad.exe", "test.txt", "C:\", 1) code