I'm trying to write a program which executes 2 different .exe files and outputs their results to a text file. When I execute them separately, they work fine, but when I try to execute them both, the second process doesn't run. Can anyone help?
Here is the code. Player1.exe and Player2.exe are console applications returning 0 or 1.
static void Main(string[] args)
{
Process process1 = new Process();
process1.StartInfo.FileName = "C:/Programming/Tournament/Player1.exe";
process1.StartInfo.Arguments = "";
process1.StartInfo.UseShellExecute = false;
process1.StartInfo.RedirectStandardOutput = true;
process1.Start();
var result1 = process1.StandardOutput.ReadToEnd();
process1.Close();
Process process2 = new Process();
process2.StartInfo.FileName = "C:/Programming/Tournament/Player2.exe";
process2.StartInfo.Arguments = "";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.RedirectStandardOutput = true;
process2.Start();
string result2 = process2.StandardOutput.ReadToEnd().ToString();
process2.Close();
string resultsPath = "C:/Programming/Tournament/Results/Player1vsPlayer2.txt";
if (!File.Exists(resultsPath))
{
StreamWriter sw = File.CreateText(resultsPath);
sw.WriteLine("Player1 " + "Player2");
sw.WriteLine(result1 + " " + result2);
sw.Flush();
}
}
1.
you wrote in a comment: "The program doesn't even reach to process2. I tested that by putting a breakpoint."
process1.Start() may be throwing an exception. Rather than setting a breakpoint at process2, step through the lines and find the exception. Or better yet, add a try/catch block and report an error.
2.
Another possibility is that ReadToEnd is not behaving as expected. You can Peek and see if there is more data to read by looping like this:
var result1 = new StringBuilder()
while (process1.StandardOutput.Peek() > -1)
{
result1.Append(process1.StandardOutput.ReadLine());
}
3.
If these processes don't immediately provide data and end, then you need to get them both started before you begin waiting on their StandardOutput. Call Start() on each process before doing the reads.
I don't know too much about using process, but I use this approach when I need separate things to run at once. I didn't pull in the bottom portion of your project, but check this out to see if it helps in any way.
class Program
{
static void Main(string[] args)
{
Process process1 = new Process();
process1.StartInfo.FileName = "C:/Programming/Tournament/Player1.exe";
process1.StartInfo.Arguments = "";
process1.StartInfo.UseShellExecute = false;
process1.StartInfo.RedirectStandardOutput = true;
Process process2 = new Process();
process2.StartInfo.FileName = "C:/Programming/Tournament/Player2.exe";
process2.StartInfo.Arguments = "";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.RedirectStandardOutput = true;
Thread T1 = new Thread(delegate() {
doProcess(process1);
});
Thread T2 = new Thread(delegate() {
doProcess(process2);
});
T1.Start();
T2.Start();
}
public static void doProcess(Process myProcess)
{
myProcess.Start();
var result1 = myProcess.StandardOutput.ReadToEnd();
myProcess.Close();
}
}
Related
I created a method to call ffmpeg binaries and do stuff for me. It worked perfectly fine on a standard console application. I am trying to make a Windows Form Application version but there are few problems. The app freezes (but the progress bar is still updating) when the ffmpeg process is still running. The textboxes are not being updated. I cannot move the app window. I suspect this is because of the loop and I googled some stuff and found out that I might need to do this asynchronously but how do I do that exactly.
public void ffmpeg(string ffmpeg_exe, string args)
{
Process p = new Process();
p.StartInfo.FileName = ffmpeg_exe;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.Start();
StreamReader reader = p.StandardError;
string line;
string size = "", current_duration = "", duration = "";
while ((line = reader.ReadLine()) != null)
{
if (!string.IsNullOrEmpty(line))
{
if (line.Contains("Duration") && line.Contains("bitrate") && line.Contains("start"))
{
duration = RemoveWhitespace(Between(line, "Duration:", ", start"));
totaltime.Text = duration;
}
if (line.Contains("frame=") && line.Contains("size=") && line.Contains("time="))
{
size = RemoveWhitespace(Between(line, "size=", " time"));
current_duration = RemoveWhitespace(Between(line, "time=", " bitrate"));
progressBar_main.Value = Convert.ToInt32(Math.Round(TimeSpan.Parse(current_duration.Substring(0, current_duration.Length -3)).TotalSeconds * 100 / TimeSpan.Parse(duration.Substring(0,duration.Length-3)).TotalSeconds, 3));
current_time.Text = current_duration;
filesize.Text = size;
}
}
}
p.Close();
current_time.Text = "";
filesize.Text = "";
totaltime.Text = "";
progressBar_main.Value = 0;
}
Try using Task like this :
Action action = () =>
{
//Do your streaming here
};
Then start it :
Task t2 = Task.Factory.StartNew(action);
Hope this was useful.
I have a console application (Host.exe) that is written in Delphi. It accepts stdin readln and responses to stdout by writeln.
Now, I want to use Host.exe in C# application in a way that C# gives input to Host.exe and gets the output from Host.exe
Ideally, I write the code below but it doesn't work: it hangs somewhere in the outputReader.ReadLine();
System.IO.File.WriteAllText(tmp, vbs);
Process pProcess = new Process();
pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = #"Host.exe";
pProcess.StartInfo.Arguments = "\"runa " + tmp +"\"";
// runs script file tmp in background
pProcess.StartInfo.CreateNoWindow = true;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.RedirectStandardInput = true;
pProcess.StartInfo.RedirectStandardError = true;
pProcess.Start();
StreamWriter inputWriter = pProcess.StandardInput;
StreamReader outputReader = pProcess.StandardOutput;
while (true)
{
inputWriter.WriteLine("getmsg");
inputWriter.Flush();
string s = outputReader.ReadLine(); // then do something with it
inputWriter.WriteLine("progressglobal");
inputWriter.Flush();
string p = outputReader.ReadLine();
if (p == "100")
{
break;
}
Application.DoEvents();
Thread.Sleep(1000);
}
inputWriter.WriteLine("exit");
inputWriter.Flush();
pProcess.WaitForExit();
Many thanks for any suggestions in advance !
You read the line twice:
string s = outputReader.ReadLine();
and
string p = outputReader.ReadLine();
It seems you only need the last line as the variable s is not used.
I want to do a simple app using stdin. I want to create a list in one program and print it in another. I came up with the below.
I have no idea if app2 works however in app1 I get the exception "StandardIn has not been redirected." on writeline (inside the foreach statement). How do I do what I intend?
NOTE: I tried setting UseShellExecute to both true and false. Both cause this exception.
//app1
{
var p = new Process();
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = #"path\bin\Debug\print_out_test.exe";
foreach(var v in lsStatic){
p.StandardInput.WriteLine(v);
}
p.StandardInput.Close();
}
//app 2
static void Main(string[] args)
{
var r = new StreamReader(Console.OpenStandardInput());
var sz = r.ReadToEnd();
Console.WriteLine(sz);
}
You never Start() the new process.
You must ensure ShellExecute is set to false in order for the redirection to work correctly.
You should also open a streamwriter on it, start the process, wait for the process to exit, and close the process.
Try replacing these lines:
foreach(var v in lsStatic){
p.StandardInput.WriteLine(v);
}
p.StandardInput.Close();
with these:
p.Start();
using (StreamWriter sr= p.StandardInput)
{
foreach(var v in lsStatic){
sr.WriteLine(v);
}
sr.Close();
}
// Wait for the write to be completed
p.WaitForExit();
p.Close();
if you would like to see a simple example of how to write your process to a Stream use this code below as a Template feel free to change it to fit your needs..
class MyTestProcess
{
static void Main()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false ;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"path\bin\Debug\print_out_test.exe";
p.StartInfo.CreateNoWindow = true;
p.Start();
System.IO.StreamWriter wr = p.StandardInput;
System.IO.StreamReader rr = p.StandardOutput;
wr.Write("BlaBlaBla" + "\n");
Console.WriteLine(rr.ReadToEnd());
wr.Flush();
}
}
//Change to add your work with your for loop
From http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardinput.aspx
You must set UseShellExecute to false if you want to set RedirectStandardInput to true. Otherwise, writing to the StandardInput stream throws an exception.
One might expect it to be false by default, that doesn't seem to be the case.
Hi according to my last question here I try to write a sql editor or some thing like this,in this way I try to connect to CMD from C# and execute my command.
Now my problem is I connect to SQLPLUS after that I cant get SQLPLUS command,and the other resource I review don't satisfy me. Please help me how after I connected to sqlplus , I can a live my process to run my sql command? right now I use this code:
//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = strCommand;
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = strCommandParameters;
pProcess.StartInfo.UseShellExecute = false;
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
//Optional
pProcess.StartInfo.WorkingDirectory = strWorkingDirectory;
//Start the process
pProcess.Start();
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
I customized it. I separate the initialize, I created the process object one time I still have problem, to run the second command I use these codes for the second call:
pProcess.StartInfo.FileName = strCommand;
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = strCommandParameters;
//Start the process
pProcess.Start();
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
Thanks in advance
Your question is a little confusing but I think i see your problem. First you should check out this blog post to see common issues with System.Diagnostics.Process. Your code happens to violate one that isn't listed there. The reuse of the Process object itself.
You need to refactor the code like:
class MyProcessStarter
{
private ProcessStartInfo _startInfo = new ProcessStartInfo();
public MyProcessStarter(string exe, string workingDir)
{
_startInfo.WorkingDirectory = workingDir;
_startInfo.FileName = exe;
_startInfo.UseShellExecute = false;
_startInfo.RedirectStandardOutput = true;
}
public string Run(string arguments)
{
_startInfo.Arguments = arguments;
Process p = Process.Start(_startInfo);
p.Start();
string strOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
return strOutput;
}
}
I've written a more complete and accurate implementation called ProcessRunner. The following demonstrates it's usage to perform the same operation:
using CSharpTest.Net.Processes;
partial class Program
{
static int Main(string[] args)
{
ProcessRunner run = new ProcessRunner("svn.exe");
run.OutputReceived += new ProcessOutputEventHandler(run_OutputReceived);
return run.Run("update", "C:\\MyProject");
}
static void run_OutputReceived(object sender, ProcessOutputEventArgs args)
{
Console.WriteLine("{0}: {1}", args.Error ? "Error" : "Output", args.Data);
}
}
You need to READ ALL data from input, before send another command!
And you can't ask to READ if no data is avaliable... little bit suck isn't?
My solutions... when ask to read... ask to read a big buffer... like 1 MEGA...
And you will need wait a min 100 milliseconds... sample code...
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim oProcess As New Process()
Dim oStartInfo As New ProcessStartInfo("cmd.exe", "")
oStartInfo.UseShellExecute = False
oStartInfo.RedirectStandardOutput = True
oStartInfo.RedirectStandardInput = True
oStartInfo.CreateNoWindow = True
oProcess.StartInfo = oStartInfo
oProcess.Start()
oProcess.StandardInput.WriteLine("dir")
Threading.Thread.Sleep(100)
Dim Response As String = String.Empty
Dim BuffSize As Integer = 1024 * 1024
Dim bytesRead As Integer = 0
Do
Dim x As Char() = New Char(BuffSize - 1) {}
bytesRead = oProcess.StandardOutput.Read(x, 0, BuffSize)
Response = String.Concat(Response, String.Join("", x).Substring(0, bytesRead))
Loop While oProcess.StandardOutput.Peek >= 0
MsgBox(Response)
Response = String.Empty
oProcess.StandardInput.WriteLine("dir c:\")
Threading.Thread.Sleep(100)
bytesRead = 0
Do
Dim x As Char() = New Char(BuffSize - 1) {}
bytesRead = oProcess.StandardOutput.Read(x, 0, BuffSize)
Response = String.Concat(Response, String.Join("", x).Substring(0, bytesRead))
'Response = String.Concat(Response, String.Join("", x))
Loop While oProcess.StandardOutput.Peek >= 0
MsgBox(Response)
End Sub
End Class
I have a GUI application within which i'm spawning a console application using Process class.
Process p1 = new Process();
p1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p1.StartInfo.CreateNoWindow = true;
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.FileName = Path.Combine(basepath, "abc.exe");
p1.StartInfo.Arguments = "/pn abc.exe /f \"temp1.txt\"";
p1.StartInfo.RedirectStandardError = true;
p1.StartInfo.RedirectStandardInput = true;
p1.StartInfo.RedirectStandardOutput = true;
p1.OutputDataReceived += new DataReceivedEventHandler(outputreceived);
p1.ErrorDataReceived += new DataReceivedEventHandler(errorreceived);
p1.Start();
tocmd = p1.StandardInput;
p1.BeginOutputReadLine();
p1.BeginErrorReadLine();
Now i have a problem that, though it reads the console output asynchronously but it seems to fire the event only when the internal buffer is filled with some amount. I want it to display data as it comes. If there's 10 bytes in buffer, let it display the 10 bytes. My program implements sleep() call internally, so i need to print the data till it goes to sleep.
How can i do it?
=============
As it was mentioned the output is line buffered, i tried the following change in the code
p1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p1.StartInfo.CreateNoWindow = true;
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.FileName = Path.Combine(basepath, "abc.exe");
p1.StartInfo.Arguments = pnswitch + " /f \"temp1.txt\"";
p1.StartInfo.RedirectStandardError = false;
p1.StartInfo.RedirectStandardInput = true;
p1.StartInfo.RedirectStandardOutput = true;
p1.Start();
tocmd = p1.StandardInput;
MethodInvoker mi = new MethodInvoker(readout);
mi.BeginInvoke(null, p1);
and inside readout i wrote
void readout()
{
string str;
while ((str = p1.StandardOutput.ReadLine()) != null)
{
richTextBox1.Invoke(new UpdateOutputCallback(this.updateoutput), new object[] { str });
p1.StandardOutput.BaseStream.Flush();
}
}
So i think it now monitors when each line is written and it prints it right? this too didn't work. Any thing wrong there?
The Output and Error Data received is line buffered, and will only fire when a newline is added.
Your best bet is to use you own reader that can read the input, byte by byte. Obvioulsly, this would have to be non-blocking :)
In order to achieve this, you must use synchronous read operations on a redirected stream.
Your code would look like this (MSDN Sample):
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
**string output = p.StandardOutput.ReadToEnd();**
p.WaitForExit();
In order to achieve async behavior you would have to use some threads.
MSDN Article here