RAPID PUBLISHING ARTICLES PROVIDE INFORMATION DIRECTLY FROM WITHIN THE MICROSOFT SUPPORT ORGANIZATION. THE INFORMATION CONTAINED HEREIN IS CREATED IN RESPONSE TO EMERGING OR UNIQUE TOPICS, OR IS INTENDED SUPPLEMENT OTHER KNOWLEDGE BASE INFORMATION.
The Windows Script Host object model provides the WshShell.Exec method, which allows script to run an application in a child command-shell. The Exec method returns a WshScriptExec object, which exposes the StdOut and StdErr properties. These properties provide access to information the child process has written to its standard output or standard error streams.
When reading the StdErr or StdOut properties of the WshScriptExec object, the script may hang (deadlock).
A console application's StdOut and StdErr streams share the same internal 4KB buffer. In addition, the WshScriptExec object only provides synchronous read operations on these streams. Synchronous read operations introduce a dependency between the calling script reading from these streams and the child process writing to those streams, which can result in deadlock conditions.
When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits on the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits on the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait on each other to complete an operation, and neither can proceed. Generally, this occurs if the spawned application is writing to both the StdOut and StdErr streams, while the calling script is architected so that it reads only from one of them.
These are several possible workarounds.
1. Architect the script so that it reads from both StdOut and StdErr, which prevents the buffer from filling up.
2. Redirect the console program output to a disk file (or files), and then read the file(s). When spawning the console application, you can use the ">" operator to output the results of the operation to a file. You could then use the FileSystemObject to read the output files back in for processing.
3. Instead of using the WScript object model for reading the StdOut/StdErr streams, write a COM component that implements asynchronous reads of these streams; and then call the COM component from your script. For example, the System.Diagnostics.Process class in the Microsoft .NET Framework exposes an asynchronous model for reading the StdOut/StdErr streams. Therefore; it is possible to write a COM callable component which utilizes the Microsoft .NET Framework which could raise event back to the script on read operations.
1. Create a new text file with the following script.
'This script will simulate the work done by a console application. 'This just iterates 500 times, writing data to both the StdOut and StdErr streams. Dim i For i = 1 to 500 WScript.StdOut.WriteLine "StdOut: " & i WScript.StdErr.WriteLine "StdErr: " & i Next
2. Save the file as "scriptworker.vbs". 3. Create a new text file with the following script.
Dim WSHShell Dim oExec
Set WSHShell = CreateObject("WScript.Shell") Set oExec = WSHShell.Exec("cscript scriptworker.vbs") With oExec Do While .Status = 0 WScript.Sleep 10 Do While Not .StdOut.AtEndOfStream WScript.Echo .StdOut.ReadLine Loop Loop End With
4. Save this file as "scriptspawn.vbs", and place it in the same folder with scriptworker.vbs. 5. Start a new command prompt (cmd.exe). 6. Navigate to the folder where you copied scriptworker.vbs and scriptspawn.vbs. 7. Run the following command:
Note that the script hangs after outputting just over 300 items, and you have to manually kill the process by pressing CTRL+C or CTRL+BREAK.
Steps to Work Around Problem =======================
This example demonstrates how to change the script to read from both StdOut and StdErr, which prevents the buffer from filling up.
1. Follow the steps in the "Steps to Reproduce Problem" section above. 2. Use the following script for scriptspawn.vbs, rather than the script shown in the "Steps to Reproduce Problem" section.
Dim WSHShell Dim oExec
Set WSHShell = CreateObject("WScript.Shell") Set oExec = WSHShell.Exec("cscript scriptworker.vbs") With oExec Do While .Status = 0 WScript.Sleep 10 Do While Not .StdOut.AtEndOfStream WScript.Echo .StdOut.ReadLine
'Check the .StdErr to see if it is at the end of its 'stream. If not, call ReadLine on it If Not .StdErr.AtEndOfStream Then .StdErr.ReadLine End If Loop Loop End With
Note that when running this script, it does not hang and successfully reads all data from the StdOut property.
MICROSOFT AND/OR ITS SUPPLIERS MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY, RELIABILITY OR ACCURACY OF THE INFORMATION CONTAINED IN THE DOCUMENTS AND RELATED GRAPHICS PUBLISHED ON THIS WEBSITE (THE “MATERIALS”) FOR ANY PURPOSE. THE MATERIALS MAY INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS AND MAY BE REVISED AT ANY TIME WITHOUT NOTICE.
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT AND/OR ITS SUPPLIERS DISCLAIM AND EXCLUDE ALL REPRESENTATIONS, WARRANTIES, AND CONDITIONS WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO REPRESENTATIONS, WARRANTIES, OR CONDITIONS OF TITLE, NON INFRINGEMENT, SATISFACTORY CONDITION OR QUALITY, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH RESPECT TO THE MATERIALS.