This is a follow-up to a thread where I said I'd look at controlling applications which had been started using the Exec method of WScript.Shell

The bad news is that the method and properties in this object are pretty brain-dead, so it is of very little use. I've documented what I've found here to save anyone else wasting their time on it.

There are two main problems.

The first problem is determining whether there is any output available on the standard out or standard error streams.

None of the available methods or properties will report how much data is available on either stream, which means that when you try to read a byte and nothing is available the read blocks.

As there is no option for non-blocking I/O, and there is no timeout or alarm that can be used to wake-up or interrupt the thread this means that you must be absolutely certain of all the output conditions which might occur. If you do not get expected output then process will simply hang. This can be mitigated by spawning a seperate watchdog process to kill the Exec'd program if it hangs, but this is not ideal.

The second and more serious problem is that output from console or command line programs does not always appear on the output stream. I've tried with FTP and TELNET, and in both cases no output is detectable. Bizzarely, the input stream *is* still attached, and it is possible to send commands to the programs. As the result of each command is not visible this is of limited use however.

The UDF usage is udfExpect(Command_or_Array,Send_string,Expect_strings)

udfExpect() returns an array which contains the Exec'd object, the data received so far on the output stream and the expect string which caused it to return if present.

Command_or_Array is either the command you wish to exec, or the array returned by an earlier udfExpect().

If "Send_string" is present it is sent to the command.

If "Expect_strings" are present, the UDF waits for one of the strings to appear before returning. If none of the strings appear then the UDF will wait until the Exec'd command exits. The strings are separated with an end-of-file CHR(26) character.

The UDF is in a working but unfinished state as I don't think that it is viable enough to spend any more time on it, but it may be useful to someone who wishes to experiment.

The simple example here starts a DOS session, runs the "date" command and if it is prompted for a new date enters one.

Code:
Break ON

$=SetOption("Explicit","ON")
$=SetOption("WrapAtEOL","ON")
$=SetOption("ASCII","ON")
Dim $oExpect,$sPROMPT,$aiDate

$sPROMPT="DOSPROMPT>"
SetL "PROMPT="+$sPROMPT

$aiDate=Split(@DATE,"/")

; Start a dos prompt.
$oExpect=udfExpect('"'+%COMSPEC%+'" /A',,$sPROMPT)
"Process ID is " $oExpect[0].ProcessID ?

; Run the "date" command.
$oExpect=udfExpect($oExpect,"date"+Chr(10),$sPROMPT+Chr(26)+"(dd-mm-yy)")
"EXPECT STRING FOUND WAS '"+$oExpect[2]+"'" ?
"-----START OUTPUT FROM COMMAND-----" ?
$oExpect[1] ?
"------END OUTPUT FROM COMMAND------" ?

If $oExpect[2]="(dd-mm-yy)"
; Change the date if correctly prompted.
$oExpect=udfExpect($oExpect,$aiDate[2]+"-"+$aiDate[1]+"-"+$aiDate[0]+Chr(10),$sPROMPT)
"EXPECT STRING FOUND WAS '"+$oExpect[2]+"'" ?
"-----START OUTPUT FROM COMMAND-----" ?
$oExpect[1] ?
"------END OUTPUT FROM COMMAND------" ?
Else
"Error, 'date' command not prompting for input!" ?
EndIf

"All done." ?

Exit 1

Function udfExpect($vCommand, Optional $sSend, Optional $sExpect)
Dim $sFind

; Sanity check object
If 8192 & VarType($vCommand) ; Is an array
If VarType($vCommand[0])=9 ; Is an object
Dim $vCheck
$vCheck=$vCommand[0].ProcessID
If $vCheck
$udfExpect=$vCommand
Else
"Is an object, not the right one!" ?
Exit 1
EndIf
Else
"Missing command stream object!" ?
Exit 1
EndIf
Else
If VarType($vCommand)=8 AND $vCommand <> "" ; String ok, create command stream
Dim $oShell
Redim $udfExpect[3]
$udfExpect[0]=CreateObject("WScript.Shell").Exec($vCommand)
If @ERROR Exit @ERROR EndIf
If Not VarType($udfExpect[0])=9 Exit 1 EndIf
Else
"Not an object, not a string. Not much good." ?
Exit 1
EndIf
EndIf

$udfExpect[1]=""
If VarType($sSend)
$udfExpect[0].StdIn.Write($sSend)
EndIf
If VarType($sExpect)
While $udfExpect[0].StdOut.AtEndOfStream=0
$udfExpect[1]=$udfExpect[1]+$udfExpect[0].StdOut.Read(1)
For Each $sFind in Split($sExpect,Chr(26))
If InStr($udfExpect[1],$sFind)
$udfExpect[2]=$sFind
Exit 0
EndIf
Next
Loop
EndIf

EndFunction

; vim600: filetype=kix ai sw=4 ts=4 fdc=4 fdm=marker