2

I am trying to get the output of a process in real time and at the same time save it to a variable,i tried the following looking at other stackoverflow questions C# Show output of Process in real time ,however I get an InvalidOperationException error at line StreamReader myStreamReader = myProcess.StandardOutput,what am I missing?how to fix it?

using System;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace CallPython
{
    class Program
    {
        static void Main(string[] args)
        {
            // full path of python interpreter 
            string python = @"C:\\Python27\python.exe";

            // python app to call 
            string myPythonApp = @"C:\\Dropbox\script.py";

            // dummy parameters to send Python script 

            string m = @"\\location\\build1";
            string s = "emmc";
            string a = "3980bdd4";
            string ft = "60000";
            string at = "60000";
            string bt = "120000";


            // Create new process start info 
            ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(python);

            // make sure we can read the output from stdout 
            myProcessStartInfo.UseShellExecute = false;
            myProcessStartInfo.RedirectStandardOutput = true;
            myProcessStartInfo.RedirectStandardError = true;

            // start python app with  arguments  
            //myProcessStartInfo.Arguments = myPythonApp + " " + "-m" + " " + m + " " + "-s" + " " + s + " " + "-a" + " " + a + " " + "-ft" + " " + ft + " " + "-at" + " " + at + " " + "-bt" + " " + bt;
            myProcessStartInfo.Arguments = String.Format("{0} -m {1} -s {2} -a {3} -ft {4} -at {5} -bt {6}", myPythonApp, m,s,a,ft,at,bt);

            Process myProcess = new Process();
            // assign start information to the process 
            myProcess.StartInfo = myProcessStartInfo;

            Console.WriteLine("Calling Python script with arguments {0} ,{1},{2},{3},{4},{5}", m, s,a,ft,at,bt);
            // start the process 
            myProcess.Start();

            myProcess.BeginErrorReadLine();
            myProcess.BeginOutputReadLine();

            StreamReader myStreamReader = myProcess.StandardOutput;

            string myString = myStreamReader.ReadToEnd();


            //Console.WriteLine(myString);
            //Add code for parsing based on myString

            // wait exit signal from the app we called and then close it. 
            myProcess.WaitForExit();
            myProcess.Close();
            Console.ReadLine();
        }

    }

}
Community
  • 1
  • 1
  • 1
    did you try the approach from the cited question, too? redirecting stdout and stderr streams, attaching an event handler and setting `UseShellExecute` to false? – Cee McSharpface Jul 11 '16 at 19:40

3 Answers3

1

Process.StandardOutput throws an InvalidOperationException when

The StandardOutput stream has been opened for asynchronous read operations with BeginOutputReadLine.

So you must either

  1. Read from StandardOutput
  2. Handle OutputDataReceivedEvent and call BeginOutputReadLine()

but not both.


For example, collect the output while printing each line to the console:

StreamReader reader = process.StandardOutput;
StringBuilder builder = new StringBuilder();

string line;
while ((line = reader.ReadLine()) != null)
{
    builder.AppendLine(line);
    Console.WriteLine(line);
}

string allLines = builder.ToString();
piedar
  • 2,599
  • 1
  • 25
  • 37
  • @MegaTron - If I remove all the standardoutput stream gets stored to `myString ` and then I can parse which is something I am already able to do,my question how do to both of of redirecting stdout to console and save the output to a variable –  Jul 11 '16 at 21:39
  • @PythonProg I added some code for what you want to do. – piedar Jul 12 '16 at 13:35
0

Set RedirectStandardOutput to true in order to read from the standard output.

Set RedirectStandardError to true in order to read from the standard error output.

In further details, here are the instructions that the documentation gives:

Follow these steps to perform asynchronous read operations on StandardError for a Process :

  1. Set UseShellExecute to false.
  2. Set RedirectStandardError to true.
  3. Add your event handler to the ErrorDataReceived event. The event handler must match the System.Diagnostics.DataReceivedEventHandler delegate signature.
  4. Start the Process.
  5. Call BeginErrorReadLine for the Process. This call starts asynchronous read operations on StandardError.

When asynchronous read operations start, the event handler is called each time the associated Process writes a line of text to its StandardError stream.

You can cancel an asynchronous read operation by calling CancelErrorRead. The read operation can be canceled by the caller or by the event handler. After canceling, you can call BeginErrorReadLine again to resume asynchronous read operations.

So before starting your process, you'll want to set:

myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardError = true;
myProcessStartInfo.ErrorDataReceived += (obj, args) => {
    // add code
};

I imagine you'll want to do the same thing for Standard Output.

Community
  • 1
  • 1
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • I updated the latest code based on your suggestions,now I get an exception on `StreamReader myStreamReader = myProcess.StandardOutput;` –  Jul 11 '16 at 19:57
  • @PythonProg: From the documentation I linked to: "You cannot mix asynchronous and synchronous read operations on a redirected stream. Once the redirected stream of a Process is opened in either asynchronous or synchronous mode, all further read operations on that stream must be in the same mode. For example, do not follow BeginErrorReadLine with a call to ReadLine on the StandardError stream, or vice versa. However, you can read two different streams in different modes. For example, you can call BeginErrorReadLine and then call ReadLine for the StandardOutput stream." – StriplingWarrior Jul 11 '16 at 21:31
  • if you can show what you meant with a sample code that would be perfect and helps understand better –  Jul 11 '16 at 21:43
0

Condition to InvalidOperationException in BeginErrorReadLine method:

From MSDN

In your case try to set true for

myProcess.RedirectStandardError = true;
Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
  • I updated the full code based on your suggestions ,I get an exception in `StreamReader myStreamReader = myProcess.StandardOutput;` –  Jul 11 '16 at 19:58
  • @PythonProg What kind of exception? InvalidOperationException? Try to set `false` for `UseShellExecute` or remove: `myProcess.BeginErrorReadLine(); myProcess.BeginOutputReadLine(); ` – Roman Marusyk Jul 11 '16 at 20:15
  • If I remove all the standardoutput stream gets stored to `myString ` and then I can parse which is something I am already able to do,my question how do to both of of redirecting stdout to console and save the output to a variable –  Jul 11 '16 at 21:39