1

Question

I am trying to start Python as a System.Diagnostics.Process and redirect its standard output, input and error to a RichTextBox on a Windows form, so that I can have a python REPL on my form.

The process starts correctly (Start() returns true) however no output or error data is ever sent from the process:

  • If I try to Read from the StandardOutput or StandardError the read just hangs.
  • If I instead subscribe to OutputDataReceived and ErrorDataReceived they never fire.

started with ProcessStartInfo("Python.exe")

I only see output from python once I kill it by sending Ctrl + C (sent after pressing Enter at the end of the line asd, hence the 3rd line).

To clarify, I want to see "2" sent from python (either from Reading the StandardOutput stream, or in the data event), after I pressed enter after entering "1 + 1".

Why is no data being sent to the Standard outputs and no events raised whilst python is executing? Is it possible to have this work as I want it (so that response from python is given immediately).

Additional

If instead I start cmd.exe then everything is fine and I can use the form as I would a regular command prompt. So to some extent my application is working.

However if I try to start python using this form, then it hangs until I kill the process by sending Ctrl + C as before:

started with ProcessStartInfo("cmd.exe")

Evidently the standard input is getting to python (since the line number reported by the python error message changes depending on how many lines before it)

I am using: Python 3.5.2 |Anaconda 4.2.0 (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] on win32

Again: Why does python not send any data to standard out or cause any events to be raised until it dies? How can I make it?

Code

Some of this follows the pattern of www.codeproject.com/Articles/335909/Embedding-a-Console-in-a-C-Application

Setup

var processStartInfo = new ProcessStartInfo("python.exe");
processStartInfo.UseShellExecute = false;
processStartInfo.ErrorDialog = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.CreateNoWindow = true;
process = new Process();
process.StartInfo = processStartInfo;
bool processStarted = process.Start();
outputReader = process.StandardOutput;
errorReader = process.StandardError;
inputWriter = process.StandardInput;

// start reading from standard output
outputWatchWorker.WorkerSupportsCancellation = true;
outputWatchWorker.DoWork += (o, e) =>
{
    while (!outputWatchWorker.CancellationPending)
    {
        int nChars = 0;
        string str;
        char[] buffer = new char[1024];
        nChars = outputReader.Read(buffer, 0, buffer.Length);
        if (nChars > 0)
        {
            str = new string(buffer, 0, nChars);
            Invoke((MethodInvoker)(() =>
            {
                WriteOutput(str, Color.Black);
            }));
        }
    }
};
outputWatchWorker.RunWorkerAsync();

// I've removed an almost identical block of code for errorWatchWorker to read from the error stream

KeyDown event of RichTextBox

// some extra code here I haven't included to prevent deletion of stuff printed from standard out
// just checks the cursor position and then suppresses the event if certain keys are pressed
// I can provide if it will help

if (e.KeyCode == Keys.Return)
{
    string input = textConsole.Text.Substring(inputStart, textConsole.SelectionStart - inputStart);
    lastInput = input;
    inputStart = textConsole.SelectionStart + 1; // inluding the new line
    inputWriter.WriteLine(input);
    inputWriter.Flush();
    if (string.IsNullOrWhiteSpace(input)) e.SuppressKeyPress = true;
}

WriteOutput

public void WriteOutput(string output, Color color)
{
    if (string.IsNullOrEmpty(lastInput) == false &&
        (output == lastInput || output.Replace("\r\n", "") == lastInput))
        return;

    Invoke((Action)(() =>
    {
        //  Write the output.
        textConsole.SelectionColor = color;
        textConsole.AppendText(output);
        inputStart = textConsole.SelectionStart;
    }));
}

The click listener of the "Ctrl + C" button to send Ctrl+C

just does

inputWriter.WriteLine("\x3");

based on this question How do I send ctrl+c to a process in c#?

Community
  • 1
  • 1
ollietedder
  • 63
  • 1
  • 6

0 Answers0