3

I can understand that there has been a lot of questions on this topic but none of them could really solve my problem. So here I have presented my code, and I want my mistakes to be pointed out here.

I have a program written in C# which shall call a python executable/file. The first requirement is that I have pass one argument to the python file via the input stream. This I could do. The real problem I am facing now is that, I have to see whether my python file is printing "Please enter argument_x", I have to read this output in my C# code and check if it is argument_x, then only write the argument value in the input stream. Below are the code snippets for C# and Python.

The C# code is as follows:

using System;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //Create a new process object
            Process myProcess = new Process();

            //Provide the start information for the process
            myProcess.StartInfo.FileName = "python.exe";
            myProcess.StartInfo.Arguments = "mytestpython.py";
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.RedirectStandardInput = true;
            myProcess.StartInfo.RedirectStandardOutput = true;

            StreamReader myStreamReader;
            StreamWriter myStreamWriter;

            //Invoke the process from current process
            myProcess.Start();

            myStreamReader = myProcess.StandardOutput;

            //Read the standard output of the spawned process.
            string myString = myProcess.StandardOutput.ReadToEnd();
            Console.WriteLine(myString);

            if (myString.Contains("argument_x"))
            {
                myStreamWriter = myProcess.StandardInput;
                String argument = "argument_value";
                myStreamWriter.WriteLine(argument);
            }

           myProcess.WaitForExit();
           myStreamWriter.Close();
           myStreamReader.Close();
           myProcess.Close();
      }
   }
}

The python program in mytestpython.py file looks like this:

import sys
import getpass
prompt_string = "Please enter argument_x"
if sys.stdin.isatty():
    reqd_arg = getpass.getpass(prompt=prompt_string)
else:
    print(prompt_string)
    reqd_arg = sys.stdin.readline().rstrip()

Please help me out, as I feel I have written 90% of the code correctly with a minor mistake somewhere in between. Thanks in advance for your help.

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
PRC_noob
  • 74
  • 1
  • 5
  • 1
    Make sure you flush after each output or it won't work correctly. – Daniel Jun 07 '12 at 21:25
  • @Dani Could you please tell me which function to use? Autoflush or something? And where exactly? – PRC_noob Jun 07 '12 at 21:27
  • A line After `myStreamWriter.WriteLine` put `myStreamWriter.Flush();` – Daniel Jun 07 '12 at 21:28
  • @Dani This did not particularly help out.Please provide other solutions to the existing problem,if any.Meanwhile I am also working on it and trying to understand what might be causing the problem. – PRC_noob Jun 07 '12 at 21:42
  • @Dani Finally found out the solution, I had to change both Python and C# code to accomodate this. What I had I had to do in Python was to flush stdout after the print statement.At C# end, I had to just do Readline as long as I get my desired string. For my operation, both ReadToEnd and BeginOutputReadline was not suitable.Will be posting the working code very soon. Immense thanks for your inputs :) – PRC_noob Jun 19 '12 at 18:13

3 Answers3

4

When you do myProcess.StandardOutput.ReadToEnd();, it tries to read to the end of the stdout of your Python program, meaning it will wait for the Python program to finish executing and close its stdout stream, which it never does because it's waiting for input from your C# program. This results in a deadlock.

univerio
  • 19,548
  • 3
  • 66
  • 68
  • You are absolutely right.But I really need to wait for the input stream to be accepted by python process only when the output stream token "argument_x" matches it.Please provide any solution,given the above piece of code,if possible. – PRC_noob Jun 07 '12 at 21:37
  • @PRC_noob You need to read character-by-character and try to match what you read and what you expect. – univerio Jun 07 '12 at 21:45
4

ReadToEnd() is useful when the parent process waits for child process to finish. In case of interactive process communication, you should really consider using asynchronous communications using BeginOutputReadLine check the MSDN documentation here for help

GETah
  • 20,922
  • 7
  • 61
  • 103
  • 2
    @PRC_noob Please don't forget to give some reward (+1) to all who have helped you out through your problem :) And accept the answer you see very close to the solution you found out; – GETah Jun 19 '12 at 18:50
2

I modified the C# code to accept CLI params and to pass the password on a prompt as follows:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Diagnostics;
    using System.ComponentModel;

    namespace Stub
    {
      class Program
        {
          static void Main(string[] args)
            {
            // Declare variables and initialize
            string passWord = string.Empty;
            string processArgs = getArguments(args); //Call getArguments method

            Console.Write("Please enter the system password : ");
            passWord = readPassword(); //call readPassword method

            Process p = new Process();

            p.StartInfo.FileName = "myexe.exe";
            p.StartInfo.Arguments = processArgs;
            p.StartInfo.WorkingDirectory = "my_working_directory";

            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;

            p.Start();
            StreamWriter sw = p.StandardInput;
            StreamReader sr = p.StandardOutput;

            writePassword(sr, sw, "password", passWord);

            sw.Close();
            sr.Close();
            p.WaitForExit();
            p.Close();
    }

    static string getArguments(string[] args)
    {
        StringBuilder procArgs = new StringBuilder();
        foreach (string arg in args)
        {
            procArgs.Append(arg);
            procArgs.Append(" ");
        }
        return procArgs.ToString();
    }



    static void writePassword(StreamReader sr, StreamWriter sw, string keyWord, string passWord)
    {
        string mystring;            
        do
        {
            mystring = sr.ReadLine();
        } while (!mystring.Contains(keyWord));
        if (mystring.Contains(keyWord))
            sw.WriteLine(passWord);
        else
            sw.WriteLine("\r\n");
    }

    static string readPassword()
    {
        string pass = string.Empty;
        ConsoleKeyInfo key;

        do
        {
            key = Console.ReadKey(true);
            if (key.Key != ConsoleKey.Backspace)
            {
                pass +=key.KeyChar;
                Console.Write("*");
            }
            else
            {
                if (pass.Length > 0)
                {
                    pass = pass.Substring(0, (pass.Length - 1));
                    Console.Write("\b \b");
                }
            }
        } while (key.Key != ConsoleKey.Enter);

        return pass;
    }
}

}

And then just a small modification in Python as :

    import sys
    import getpass
    prompt_string = "Please enter password"
    if sys.stdin.isatty():
        reqd_arg = getpass.getpass(prompt=prompt_string)
    else:
        print(prompt_string)
        sys.stdout.flush()
        reqd_arg = sys.stdin.readline().rstrip()

And voila..that worked !!!

PRC_noob
  • 74
  • 1
  • 5