NicoGDF
NicoGDF

Reputation: 97

Communicate with process in C#

I need to communicate with external executable (ampl.exe) using standard input and standard output. This exe make calculations during some minutes with some display in the console. It has a prompt so I can succesively launch calculations by using its standard input as soon as a calculation is finished.

The external exe is launched as :

var myProcess = new Process();
myProcess.StartInfo = new ProcessStartInfo("ampl.exe");
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.Start();

I communicate with it by using myProcess.StandardInput and myProcess.StandardOutput (synchronous way).

I use standard input to launch the calcul, for example :

myProcess.StandardInput.WriteLine("solve;");

I want to wait the end of the solve statement, get results in files, prepare new calculation input files and then launching a second solve.

My problem is that I do now know when the first calculation is finished, that is when the exe is waiting for new command in its standard input. The only way I found is to add a specific display command and wait for getting it it its standard output :

    myProcess.StandardInput.WriteLine("solve;");
    myProcess.StandardInput.WriteLine("print 'calculDone';");
    string output = myProcess.StandardOutput.ReadLine();
    while (!output.Contains("calculDone"))
    {
        output = myProcess.StandardOutput.ReadLine();
    }

Is there another way avoiding to use this display command to do this ?

Edit : following advices, I tried the asynchronous way. But I still need to print 'CalculDone' to know when the solve statement ended. I do not get the prompt of ampl.exe (which is 'ampl : ') in the standard output of the process.

AutoResetEvent eventEnd = new AutoResetEvent(false);

var myProcess = new Process();
myProcess.StartInfo = new ProcessStartInfo("ampl.exe");
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.EnableRaisingEvents = true;
myProcess.OutputDataReceived += (sender, e) =>
{
    if (e.Data == "commandDone")
    {
        eventEnd.Set();
    }
    else if (e.Data != null)
    {
        Console.WriteLine("ampl: {0}", e.Data);
    }
};

myProcess.Start();
myProcess.BeginOutputReadLine();

myProcess.StandardInput.WriteLine("solve;");
myProcess.StandardInput.WriteLine("print 'commandDone';");

eventEnd.WaitOne();

Upvotes: 1

Views: 1672

Answers (1)

InBetween
InBetween

Reputation: 32740

The best option would be to use the Processs.OutputDataReceived event instead of a tight while loop. It’s like the event async pattern, you launch an asynchronous task and wait for an event callback telling you it’s done. The continuation of the asynchronous task would go in the event handler. Remember to unsubscribe the event handler the first time it goes off, otherwise it will be firing when you don’t want it to.

Another option I have never tried is Process.WaitForInputIdle() method, but I’m not sure if this will work in your particular case. If it does you wouldn’t need to write anything to the input stream.

Upvotes: 1

Related Questions