Hang When Reading StdErr/StdOut Properties of WshScriptExec Object
Hang When Reading StdErr/StdOut Properties of WshScriptExec Object
RAPID PUBLISHING
Symptoms
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.
Cause
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.
Resolution
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.
FileSystemObject Object
http://msdn.microsoft.com/en-us/library/z9ty6h50(VS.85).aspx (http://msdn.microsoft.com/en-us/library/z9ty6h50(VS.85).aspx)
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.
Process.StandardOutput Property
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx (http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx)
Process.StandardError Property
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standarderror.aspx (http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standarderror.aspx)
ÂÂ
Advanced Steps
Steps to Reproduce Problem
=====================
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:
cscript.exe scriptspawn.vbs
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.
DISCLAIMER
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.
Microsoft Knowledge Base Article
This article contents is Microsoft Copyrighted material.
Microsoft Corporation. All rights reserved. Terms of Use | Trademarks
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Back to the top
Leave a Reply