=====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: SYS "ShellExecute", @hwnd%, 0, prog$, parm$, cdir$, show% Here **prog$** is the name of the application you want to run (e.g. ""NOTEPAD.EXE""), **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: SYS "ShellExecute", @hwnd%, 0, "notepad.exe", "test.txt", "C:\", 1 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: 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 So, by analogy with the example above, you might call this procedure as follows: PROCexecuteandwait("notepad.exe", "test.txt", "C:\", 1) 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: 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% You might call it as follows: exitcode% = FNexecuteandwait("notepad.exe", "test.txt", "C:\", 1)