Suppress output of child process - c#

Let's say we start console application with:
public static void StartProcess()
{
using var next = new Process();
next.StartInfo.UseShellExecute = false;
next.StartInfo.FileName = "dotnet";
next.StartInfo.Arguments = "/opt/ConsoleApp1/ConsoleApp1.dll";
next.Start();
}
This code leads to double StandardOutput and StandardError, because parent and child processes will write data to the same terminal. How to supress child process output and/or detach child console?
Of course I can do something like this:
public static void StartProcess()
{
using var next = new Process();
next.StartInfo.UseShellExecute = false;
next.StartInfo.FileName = "dotnet";
next.StartInfo.Arguments = "/opt/ConsoleApp1/ConsoleApp1.dll";
next.StartInfo.RedirectStandardOutput = true;
next.StartInfo.RedirectStandardError = true;
next.Start();
next.StandardOutput.BaseStream.CopyToAsync(Stream.Null);
next.StandardError.BaseStream.CopyToAsync(Stream.Null);
}
As far as I understand this will work until parent process is alive, but what if child process will work longer than parent one? Need some stable, cross-platform solution.

Adding StartInfo.CreateNoWindow = true fixed it for me.
Well, almost... Starting the process still spits out a newline onto the Linux console, but no other output.

Related

Unity C#: Communicate with console program asynchronously

I'm currently trying to write a chess GUI in Unity. I have a chess engine as an exe-file which communicates via UCI (console). The program is in a very early stage and I just want to communicate with the chess engine (console application) as a proof of concept.
At the moment I'm using the Process class with a redirected output and input. Everything that the chess engine outputs is currently being printed in the unity console. Unfortunately nothing I try works like expected.
How the Chess Engine (Console Program) works:
User can write a command in the console e.g. "uci" or "go dept 1"
The engine will respond with "uciok" or "bestmove e2e4"
These responses can also have multiple lines.
What I want:
Press play in Unity
My script instantly sends the message "uci" to the chess engine
The output (muliple lines) gets printed to the unity console
Everytime I press 'c' step 2 and 3 get repeated
What currently happens:
Press play in Unity
Console (chess engine) opens
I manually have to close the console window
The message "uci" gets sent to the chess engine
Multiple lines get outputted to the unity console
When I press 'c' I'm getting an IOException
What I've also tried:
startInfo.CreateNoWindow = true; --> I have to close unity with the task manager as it waits until the process finishes
engineProcess.WaitForExit(); --> Does not work, as the process is not supposed to exit
engineProcess.BeginOutputReadLine(); --> I'm getting spammed by unity with exceptions because I'm mixing synchronus and asynchronous operations
Many other solutions already suggested on StackOverflow. Nothing worked so far :(
I'm pretty new to Unity and C#, so I hope that this question does not make me look stupid. I posted my code below.
public string pathToExe = "PathToExe";
private Process engineProcess;
//gets automatically called once when I press start in Unity
private void Start() {
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.FileName = pathToExe;
//Start process and write "uci" to chess engine
try {
engineProcess = Process.Start(startInfo);
engineProcess.StandardInput.WriteLine("uci");
}
catch {
UnityEngine.Debug.Log("Error when starting Engine");
}
}
//gets called every tick when Unity is running
private void Update() {
//write uci to chess engine when c is pressed
if (Input.GetKeyDown(KeyCode.C)) {
engineProcess.StandardInput.WriteLine("uci");
}
//read engine output if it exists
while (!engineProcess.StandardOutput.EndOfStream) {
string line = engineProcess.StandardOutput.ReadLine();
UnityEngine.Debug.Log(line);
}
}
Thanks to #BugFinder to pointing out that there are events that you can attach a method to. Using your tipps and the documentation of Process.BeginOutputReadLine I was able to solve the problem.
I commented where I added or removed line in comparison with the original code.
public string pathToExe;
private Process engineProcess;
private void Start() {
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.FileName = pathToExe;
try {
engineProcess = Process.Start(startInfo);
engineProcess.StandardInput.WriteLine("uci");
}
catch {
UnityEngine.Debug.Log("Error when starting Engine");
}
//Added these two lines so the method "engineOutputHandler"
//gets automatically called every time an output is received
engineProcess.OutputDataReceived += engineOutputHandler;
engineProcess.BeginOutputReadLine();
}
private void Update() {
if (Input.GetKeyDown(KeyCode.C)) {
engineProcess.StandardInput.WriteLine("uci");
}
//removed the entire while loop
}
//Added this method, which prints out the data received
private void engineOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) {
if (!string.IsNullOrEmpty(outLine.Data)) {
UnityEngine.Debug.Log(outLine.Data);
}
}

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();
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.

Best way to repeat a process and continue even if it fails

I have a program that restores databases. Basically the program reads from a file and restores one database at a time. The program runs 1 time per database. I need a way to repeat this until all databases are restored. Even if the database restore isn't successful, i need the process to repeat until all databases are attempted to be restored. I am thinking of having another program call the program I just mentioned. So something like this:
public const string databaseAttemptedRestore = "C:\\Logs\\AttemptedDatabaseRestore.log";
public const string databasesToRestore = "C:\\Logs\\DatabasesToRestore.log";
static void Main(string[] args)
{
//Repeats restore, reading from databasesToRestore. Needs to be called by task scheduler
Action toRepeat = () =>
{
var proc = new Process();
//database restore program
proc.StartInfo.FileName = #"C:\Projects\DbRestoreProdConsole\bin\Debug\DbRestoreProdConsole.exe";
// set up output redirection
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.EnableRaisingEvents = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.WorkingDirectory = #"C:\Windows\SysWOW64";
// see below for output handler
proc.Start();
proc.WaitForExit();
};
double lineCount = File.ReadLines(databasesToRestore).Count();
double repeat = Math.Ceiling(lineCount / 2);
Enumerable.Range(0, (int)repeat).ToList().ForEach(i => toRepeat());
}
}
}
I am trying to figure out the best solution to this repeating process where I can track errors and run the program smooth etc. The program runs after hours so it doesn't need to be a fast running program. The most databases I will restore per night is around 8. but it usually will be 1 or 2 per night. Any suggestions is greatly appreciated. Thank you.

cant get process error output using process.ErrorDataReceived c#

I've built Form App that I use for some time , Now I want to Catch the StandardError of my process as well as its standartOutput
I've looked at answers in SO and MSDN and yet and cant get it right
My code :
public void RunProcess(string FileName, string Arguments,, bool IsPrintOutput = true)
{
process = new Process();
process.ErrorDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
if (IsPrintOutput) process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = FileName;
process.StartInfo.Arguments = Arguments;
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);
}
process.Start();
process.BeginOutputReadLine();
//run polling on stored logs to print them to screen
PollingService();
}
I've check it with Iperf and I see that when I run it with correct argument I get correct output
but when I just send it with out any argumnet I see that with cmd I get
C:\>iperf.exe
Usage: iperf [-s|-c host] [options]
Try `iperf --help' for more information.
And my App I get Nothing !
what am I missing here ?
Thanks
You can stop reading here ! If you want to see the details of inner method continue below :
private void OnDataReceivedEvent(object sender, DataReceivedEventArgs e)
{
string ProcessOutput = e.Data;
ProcessLog.Add(e.Data);
}
private void PollingService()
{
var T = new Thread (()=>
{
while (true /* ProcessRunning*/)
{
if (ProcessLogIndex < ProcessLog.Count)
{
lock (this)
{
var tempList = ProcessLog.GetRange(ProcessLogIndex, ProcessLog.Count - ProcessLogIndex);
ProcessLogIndex = ProcessLog.Count;
foreach (var ToSend in tempList)
{
onDataOutputFromProcess(this, ToSend, sProcessNameID.ToString());
}
}
}
Thread.Sleep(400);
}
});
T.IsBackground = true;
T.Start();
}
I don't see a call to BeginErrorReadLine() anywhere in the code you posted. If you don't call that method, then the Process class won't actually redirect the stderr to your event handler.
I believe the above is the issue, but if you are actually calling that somewhere (and just didn't show it), then it is worth considering that there are some strange console programs out there that don't actually used stderr (or stdout) for error output. Instead, they write directly to the console window or some other non-standard mechanism. In those cases, you won't be able to receive the error output by redirecting stderr.
You can identify those programs by redirecting their output at the command like with e.g. iperf.exe 2> foo.txt. The stderr file handle is 2, and so that syntax redirects that file handle to a file named foo.txt. If the file is empty and you see errors on the screen, then the program is one of those strange programs.
But really, I think you probably just forgot to call BeginErrorReadLine(). :)

Process created by Process.Start() terminates when the parent application is closed

I'm using C# and Mono 2.10.2 on Debian 6.
So the scenario is that I created a process with Process.Start() like the following:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.WorkingDirectory = "/home/lucy/";
p.StartInfo.FileName = "/bin/sh";
p.StartInfo.Arguments = "/home/lucy/test.sh";
p.EnableRaisingEvents = true;
p.ErrorDataReceived += new DataReceivedEventHandler(ShellProc_ErrorDataReceived);
p.Start();
The shell script which is in this case called test.sh is ran which does several things including starting a java application. The problem I am recieving is when the c# application is terminated the bash script/java application also terminates.
I have looked at several other similar questions posted here on Stack Overflow and none come to an obvious conclusion, including this one:
How to create a Process that outlives its parent
According to some users and supposedly the docs, processes created by Process.Start() should not be terminated when the application terminates, but obviously in my case that is not true. So could this be a Mono related issue and if that is indeed the case then is there any alternatives to how I'm doing it now as I am out of ideas.
Here is a complete sample that works for me:
using System;
using System.Diagnostics;
class Tick {
static void Main(string[] args) {
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.WorkingDirectory = Environment.CurrentDirectory;
p.StartInfo.FileName = "/bin/sh";
p.StartInfo.Arguments = "test.sh";
p.EnableRaisingEvents = true;
p.ErrorDataReceived += new DataReceivedEventHandle(ShellProc_ErrorDataReceived);
p.Start();
System.Threading.Thread.Sleep (5000);
Console.WriteLine ("done");
}
static void ShellProc_ErrorDataReceived (object sender, DataReceivedEventArgs ea)
{
}
}
and then test.sh is:
while true; do
date;
sleep 1;
done
When I run the sample from a terminal, the test.sh script will continue to output data after the sample program has exited.
Update 1/Solution: This actually was not mono's fault and was indeed my own fault, the answer below helped me come to the conclusion that it was something else in my application that was causing the processes started by the application to terminate when the application terminates and the actual thing that was causing this was some GC stuff, specifically GC.Collect(), my fault, sorry and I hope this helps anybody who has a similar problem.

Categories