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.
Related
I am trying to run an R script using Rscript.exe from C#. The R script has arguments. How to invoke it? R script has a lot of optional parameters. Passing named parameters or key vlue pairs would be better.
I don't know how to pass parameters. Here's what I tried.
[Fact]
public void CEP_Abbreviated_SystemTest()
{
using (Process myProcess = new Process())
{
//(UseShellExecute) refers to a graphical shell
myProcess.StartInfo.UseShellExecute = false;
// You can start any process, HelloWorld is a do-nothing example.
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string dllPath = Uri.UnescapeDataString(uri.Path);
var digitalPlatformPath = Path.Combine(Path.GetDirectoryName(dllPath), #"..\..\..\..\..\..\");
var path = Path.Combine(Path.GetDirectoryName(dllPath), #"..\..\..\..\..\..\", "lib", "R-3.6.1", "bin", "Rscript.exe");
var arg = Path.Combine(digitalPlatformPath, "DP.RPackages", "ClientEngagementPipeline", "exec", "CEP_Main.R");
myProcess.StartInfo.FileName = path;
myProcess.StartInfo.WorkingDirectory = Path.GetDirectoryName(path);
myProcess.StartInfo.CreateNoWindow = true;
//myProcess.StartInfo.Arguments = $"{arg} --tokens env.shruti --cluster cluster.local --localDir C:/Users/ShrutiKulkarni/Documents/1_DP/CEP/Winn/Job1 --releaseVersion 2.2 --scenarioConfigName ScenarioConfiguration_1MA.csv --newScenarioVersion Test_Winn --overwrite true --portfolioImporterFile C:/Users/ShrutiKulkarni/Documents/1_DP/CEP/Winn/Job1/ReadOnly_Files/ImporterFiles/PPA_OneRowWithDevGenFile-Valid_Solar.csv";
myProcess.StartInfo.Arguments = $"{arg} ";
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.Start();
myProcess.WaitForExit();
Assert.True(myProcess.ExitCode != -1, myProcess.StandardOutput.ReadToEnd());
// This code assumes the process you are starting will terminate itself.
// Given that is is started without a window so you cannot terminate it
// on the desktop, it must terminate itself or you can do it programmatically
// from this application using the Kill method.
}
}
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();
}
}
I tried using the Process class as always but that didn't work. All I am doing is trying to run a Python file like someone double clicked it.
Is it possible?
EDIT:
Sample code:
string pythonScript = #"C:\callme.py";
string workDir = System.IO.Path.GetDirectoryName ( pythonScript );
Process proc = new Process ( );
proc.StartInfo.WorkingDirectory = workDir;
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.FileName = pythonScript;
proc.StartInfo.Arguments = "1, 2, 3";
I don't get any error, but the script isn't run. When I run the script manually, I see the result.
Here's my code for executing a python script from C#, with a redirected standard input and output ( I pass info in via the standard input), copied from an example on the web somewhere. Python location is hard coded as you can see, can refactor.
private static string CallPython(string script, string pyArgs, string workingDirectory, string[] standardInput)
{
ProcessStartInfo startInfo;
Process process;
string ret = "";
try
{
startInfo = new ProcessStartInfo(#"c:\python25\python.exe");
startInfo.WorkingDirectory = workingDirectory;
if (pyArgs.Length != 0)
startInfo.Arguments = script + " " + pyArgs;
else
startInfo.Arguments = script;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardInput = true;
process = new Process();
process.StartInfo = startInfo;
process.Start();
// write to standard input
foreach (string si in standardInput)
{
process.StandardInput.WriteLine(si);
}
string s;
while ((s = process.StandardError.ReadLine()) != null)
{
ret += s;
throw new System.Exception(ret);
}
while ((s = process.StandardOutput.ReadLine()) != null)
{
ret += s;
}
return ret;
}
catch (System.Exception ex)
{
string problem = ex.Message;
return problem;
}
}
Process.Start should work. if it doesn't, would you post your code and the error you are getting?
You forgot proc.Start() at the end. The code you have should work if you call Start().
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
I need to write a component for an application that interacts tightly with a command line application. The command line application asks a series of questions, performs some computations, then terminates (which i need to detect). Essentially, i want to wrap up this interaction in a wrapper class.
Has any one achieved similar in the past? If so, how did you go about it? Did you notice a pattern, or maybe some good build in classes to use? Cheers!
You would need to redirect both the input and output streams, using Process; it is slightly trickier handling both, since you need to be careful that things aren't lost in the buffers (causing deadlock).
MSDN : Redirecting input
MSDN : Redirecting output
Here's a basic alternative example.
You might also want to look at OutputDataReceived for event-based responses.
I get dinged when my responses are just links to elsewhere. I don't see where the link to the C# Corner article helps much.
The question is 10 years old today but it should have been clarified. The question does not specify whether there are line endings (CrLf) at the end of each question. Assuming there are, as in the following:
string Answer;
Console.Out.WriteLine("First question: ");
Answer = Console.In.ReadLine();
Console.Out.WriteLine("Another question: ");
Answer = Console.In.ReadLine();
Console.Out.WriteLine("Final question: ");
Answer = Console.In.ReadLine();
Then the following can be used to respond to it:
class Program
{
const string FirstQuestion = "First question: ";
const string SecondQuestion = "Another question: ";
const string FinalQuestion = "Final question: ";
static AutoResetEvent Done = new AutoResetEvent(false);
static void Main(string[] args)
{
const string TheProgram = #" ... ";
Process p = new Process();
ProcessStartInfo psi = new ProcessStartInfo(TheProgram);
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
p.StartInfo = psi;
Console.WriteLine("Executing " + TheProgram);
p.Start();
DoPromptsAsync(p);
Done.WaitOne();
}
private static async Task DoPromptsAsync(Process p)
{
StreamWriter sw = p.StandardInput;
StreamReader sr = p.StandardOutput;
string Question;
Question = await sr.ReadLineAsync();
if (Question != FirstQuestion)
return;
sw.WriteLine("First answer");
Console.WriteLine(Question + "answered");
Question = await sr.ReadLineAsync();
if (Question != SecondQuestion)
return;
sw.WriteLine("Second answer");
Console.WriteLine(Question + "answered");
Question = await sr.ReadLineAsync();
if (Question != FinalQuestion)
return;
sw.WriteLine("Final answer");
Console.WriteLine(Question + "answered");
Done.Set();
}
}
The following works in a WPF application; I used a double-click event to test but this could be used in other WPF events.
const string TheProgram = #" ... ";
Process p = new Process();
ProcessStartInfo psi = new ProcessStartInfo(TheProgram);
psi.UseShellExecute = false;
//psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
p.StartInfo = psi;
p.Start();
const string FirstQuestion = "First question: ";
const string SecondQuestion = "Another question: ";
const string FinalQuestion = "Final question: ";
StreamWriter sw = p.StandardInput;
StreamReader sr = p.StandardOutput;
string Question;
StringBuilder sb = new StringBuilder("Executing " + TheProgram + "\r\n");
Question = await sr.ReadLineAsync();
if (Question != FirstQuestion)
return;
sw.WriteLine("First answer");
sb.Append(Question + "answered\r\n");
Question = await sr.ReadLineAsync();
if (Question != SecondQuestion)
return;
sw.WriteLine("Second answer");
sb.Append(Question + "answered\r\n");
Question = await sr.ReadLineAsync();
if (Question != FinalQuestion)
return;
sw.WriteLine("Final answer");
sb.Append(Question + "answered\r\n");
ResultBox.Text = sb.ToString();
I think it will be more complicated if there are not line endings after each question.
If all applications are developed in dotnet you can use Assembly class