I can run a command line process using process.start().
I can provide input using standard input. After that when the process demands user input again, how can my program know and pass the input to that exe?
There's an example here that sounds similar to what you need, using Process.StandardInput and a StreamWriter.
Process sortProcess;
sortProcess = new Process();
sortProcess.StartInfo.FileName = "Sort.exe";
// Set UseShellExecute to false for redirection.
sortProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
sortProcess.StartInfo.RedirectStandardOutput = true;
sortOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
// Redirect standard input as well. This stream
// is used synchronously.
sortProcess.StartInfo.RedirectStandardInput = true;
sortProcess.Start();
// Use a stream writer to synchronously write the sort input.
StreamWriter sortStreamWriter = sortProcess.StandardInput;
// Start the asynchronous read of the sort output stream.
sortProcess.BeginOutputReadLine();
Console.WriteLine("Ready to sort up to 50 lines of text");
String inputText;
int numInputLines = 0;
do
{
Console.WriteLine("Enter a text line (or press the Enter key to stop):");
inputText = Console.ReadLine();
if (!String.IsNullOrEmpty(inputText))
{
numInputLines ++;
sortStreamWriter.WriteLine(inputText);
}
}
hope that helps
If I understand correctly I think you should be able to do this by redirecting the Standard Input/Output using RedirectStandardOutput and RedirectStandardInput.
The WinSCP project has a C# sample for how to communicate with it using this way. You can find it here: SFTP file transfers in .NET (though this sample only collects the output without using it at all, but the technique should be the same.)
You want to look up IPC. As robert showed above, the Process class in .NET will help you. But specifically for your problem (how to know when to write data): You can't. Not generically.
If you know the required input (e.g. "yyy"), you can supply it in the STDIN of the created process. You don't have to wait for the program to request this information: It will just read from STDIN when it wants data.
If you need to process the programs output to decide what to write to STDIN, try reading the processes STDOUT. You might run into problems with flushing, though...
Related
I have a C# Console application which I am trying to execute from another WinForm application just like batch runner by giving the console application's .exe file like below.
Process.Start("Path of Console application exe to execute")
However I need to wait and handle the output and display the output in WinForm's richtextbox from console application once it has completed the execution. How can I achieve this?
Update
I have changed the code to Start a Process and Read using StandardOutput and BeginOutputReadLine() to Read the output asynchronously, but not able to see output in console window, instead console window is getting closed. Not sure how to solve this.
p.StartInfo.UseShellExecute = false;
// p.StartInfo.CreateNoWindow = True
p.StartInfo.RedirectStandardOutput = true;
string #out = null;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
#out += e.Data;
});
p.StartInfo.FileName = currentTest;
p.Start();
p.BeginOutputReadLine();
// string output = p.StandardError.ReadToEnd();
// p.WaitForExit()
while (!p.HasExited)
Application.DoEvents();
//Console.WriteLine($#"Output \n'{output.Substring(output.Length - 50)}'");
Console.WriteLine($#"\n Error stream: {#out}");
Console.ReadLine();
You need to redirect stdout (and probably stderr) so that any output comes to you, instead of a console; you may also want to redirect stdin. All of these things are available via ProcessStartInfo, with an example on MSDN. Note that if you want to display updates while the exe is running, you may need a worker thread to read incrementally from StandardOutput and StandardError, rather than ReadToEnd() - which won't return anything at all until the associated output pipe is closed.
However! If the console exe is "yours", it may be simpler to just expose the functionality you want in a library, and invoke it directly in-process. There are times when out-of-process is actively preferred, such as when you need to allow that process to go catastrophically wrong in some scenarios - but usually in-process is preferable, given free rein.
I'm creating a MVC4 web application that will have the ability to create a console that logs the output from a Powershell script to a log window using SignalR. Currently the best I have gotten to work is Capturing Powershell output in C# after Pipeline.Invoke throws. Problem is that I am not able to pass a live stream of output to the client using this method. I am only able to feed the output once the script is finished being processed. Currently i am trying this but am not receiving any output.
var loggingHub = new LoggingHub();
string powerShellExeLocation = null;
var localKey = Registry.LocalMachine;
var subKey = localKey.OpenSubKey(#"SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine");
powerShellExeLocation = subKey.GetValue("ApplicationBase").ToString();
if (!Directory.Exists(powerShellExeLocation))
throw new Exception("Cannot locate the PowerShell dir.");
powerShellExeLocation = Path.Combine(powerShellExeLocation, "powershell.exe");
if (!File.Exists(powerShellExeLocation))
throw new Exception("Cannot locate the PowerShell executable.");
if (!File.Exists(scriptFile))
throw new Exception("Cannot locate the PowerShell script.");
var processInfo = new ProcessStartInfo
{
Verb = "runas",
UseShellExecute = false,
RedirectStandardOutput = true
};
processInfo.RedirectStandardOutput = true;
processInfo.WorkingDirectory = Environment.CurrentDirectory;
processInfo.FileName = powerShellExeLocation;
//processInfo.Arguments = "-NoLogo -OutputFormat Text -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + scriptFile + "\" ";
processInfo.Arguments = build;
var powerShellProcess = new Process {StartInfo = processInfo};
powerShellProcess.Start();
while (!powerShellProcess.HasExited)
{
loggingHub.Send(DateTime.Now.ToString("h:mm:ss tt"), "info", powerShellProcess.StandardOutput.ReadLine());
}
One way to log the output as it is happening is to not only host the PowerShell engine but to also implement the host interfaces. You will then get called back for every "write" to the "host" as the PowerShell engine processes the script. Implement the host interfaces isn't trivial but it's not too hard either. Take a look at this MSDN link http://msdn.microsoft.com/en-us/windows/desktop/ee706584(v=vs.85)
From:
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx
**When a Process writes text to its standard stream, that text is typically displayed on the console. By redirecting the StandardOutput stream, you can manipulate or suppress the output of a process. For example, you can filter the text, format it differently, or write the output to both the console and a designated log file.
Note
You must set UseShellExecute to false if you want to set RedirectStandardOutput to true. Otherwise, reading from the StandardOutput stream throws an exception.
The redirected StandardOutput stream can be read synchronously or asynchronously. Methods such as Read, ReadLine, and ReadToEnd perform synchronous read operations on the output stream of the process. These synchronous read operations do not complete until the associated Process writes to its StandardOutput stream, or closes the stream.
In contrast, BeginOutputReadLine starts asynchronous read operations on the StandardOutput stream. This method enables a designated event handler for the stream output and immediately returns to the caller, which can perform other work while the stream output is directed to the event handler.
Note
The application that is processing the asynchronous output should call the WaitForExit method to ensure that the output buffer has been flushed.
Synchronous read operations introduce a dependency between the caller reading from the StandardOutput stream and the child process writing to that stream. These dependencies can cause deadlock conditions. When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits for the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits for the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait for each other to complete an operation, and neither can continue. You can avoid deadlocks by evaluating dependencies between the caller and child process.**
That seems to explain what's happening. Have you tried using the BeginOutputReadLine method instead of ReadLine?
From the MSDN example of using stdoutput of newly created process:
// This is the code for the base process
Process myProcess = new Process();
// Start a new instance of this program but specify the 'spawned' version.
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(args[0], "spawn");
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
StreamReader myStreamReader = myProcess.StandardOutput;
// Read the standard output of the spawned process.
string myString = myStreamReader.ReadLine();
Console.WriteLine(myString);
myProcess.WaitForExit();
myProcess.Close();
If instead of myStreamReader.ReadLine() I'm using myStreamReader.ReadToEnd() shall I still use myProcess.WaitForExit()?
Or ReadToEnd() will wait until the process is finished?
EDIT:
Sorry for the diversion, to directly answer your question. Yes, you need to call Process.WaitForExit();. This will ensure that the process has yielded all its output before you call ReadToEnd()
ReadToEnd is synchronous function. Hence if you don't call it in your code, it will block your main thread until it captures only the first output from the StandardOutput, then that's it. But using WaitForExit will ensure that you have everything.
Also you might consider doing an asynchronous read of the process's output, see this MSDN Example that implements OutputDataRecieved
"ReadToEnd" is a function stored in "StreamReader" object and I don't think it has something to do with waiting for a process to exit, however the "Process" class might handle that itself. By the way, all the abilities "StreamReader" has are not useful in the situation you mentioned.
In my point of view, "WaitForExit" should be called and as you did "Close" too. Because they will release some system resources that no method else can. As far as I know, "ReadToEnd" method has nothing to do with calling those two.
Cheers
I am trying to run the "dh_make" shell command, using GTK# in Mono. The problem is: dh_make requires to the process start working, to press the key . I don't know if it is possible to do using C#. Can you help me?
Yes, it definitely is possible to emulate an 'enter' pressing to a process as an input from the user. The following code would do just that, by starting dh_make as a seperate process, directing its standard input into the output of the creating process, and sending 'enter' as input to the dh_make process:
System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
myProcess.StartInfo.FileName = "dh_make";
myProcess.StartInfo.Arguments = "(put here any command line arguments you need)";
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.Start();
System.IO.StreamWriter myStreamWriter = myProcess.StandardInput;
// The following will put an 'Enter' on the first dh_make request for user input
myStreamWriter.WriteLine(" ");
I am currently using this in several pieces of code and it works very well.
Hope that helps!
As I commented, you need to have pipes communicating with the dh_make process using g_spawn_async_with_pipes. With Gtk, you probably will need to master the main event loop and adding child watching with g_child_watch_add and channel watching with g_io_add_watch. You'll probably have to use two channels (i.e. pipes), one for the input of dh_make and another one for its output.
If you have no precise idea of what pipes are, consider reading a good advanced linux programming or advanced unix programming book. Gtk's main loop is doing the multiplexing poll for you.
If you want to understand how these low-level Gtk/Glib functions are glued to Gtk#, study Gtk# documentation and eventually look inside its source code (perhaps using grep on the source code of Gtk#). Gtk# has probably some test or demo or example code related to that.
process.StartInfo.WorkingDirectory = "'/home/"+user+"/pacotes/"+nome_pacote.Text+"-1.0/'";
process.StartInfo.FileName="dh_make";
process.StartInfo.Arguments="-n -s";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.Start();
System.IO.StreamWriter myStreamWriter = process.StandardInput;
myStreamWriter.WriteLine(" ");
process.WaitForExit();
On debugging I got the following error: Odd character '>', expected a '=' after attribute name 'unknown' of element 'filename. Do you know what could be?
Is there any alternative other than Process to execute built-in shell commands in C#? Currently I am using Process class to run those commands. But in the current scenario I want to run more than 200 such commands in parallel. So spawning more than 200 processes is not a good idea. Is there any other alternative?
"Running a dos command" is the equivalent to "Creating a process and running it" so even if there is an other api, there will still be 200 processes (which, by the way, is nothing to worry about unless you're on a really, really tiny system)
You could but, shouldn't do
using Microsoft.VisualBasic;
Interaction.Shell(...);
Note: You would have to add a reference to the the VisualBasic assembly.
This is a direct answer to your question but, not somthing you should do.
As Max Keller pointed out, System.Diagnostics.Process always starts a new system process.
If have to start processes/operations for more than just some seconds, I would prefer to save all commands in a temp file and execute this with System.Diagnostics.Process rather than ever single operation.
// Get a temp file
string tempFilepath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "MyBatchFile.bat");
// Ensure the file dont exists yet
if (System.IO.File.Exists(tempFilepath)) {
System.IO.File.Delete(tempFilepath);
}
// Create some operations
string[] batchOperations = new string[]{
"START netstat -a",
"START systeminfo"
};
// Write the temp file
System.IO.File.WriteAllLines(tempFilepath, batchOperations);
// Create process
Process myProcess = new Process();
try {
// Full filepath to the temp file
myProcess.StartInfo.FileName = tempFilepath;
// Execute it
myProcess.Start();
// This code assumes the process you are starting will terminate itself!
} catch (Exception ex) {
// Output any error to the console
Console.WriteLine(ex.Message);
}
// Remove the temp file
System.IO.File.Delete(tempFilepath);