Interprocess communication between C# and Python

6.3k views Asked by At

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.

3

There are 3 answers

2
univerio On

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.

1
GETah On

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

0
PRC_noob On

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 !!!