please sorry for this stupid question, i'm newbie to c# and my Vb is untouched by many years..
Based on this article: Process Start
Here the code:
public static int Run(Action<string> output, TextReader input, string exe, params string[] args)
{
if (String.IsNullOrEmpty(exe))
throw new FileNotFoundException();
if (output == null)
throw new ArgumentNullException("output");
ProcessStartInfo psi = new ProcessStartInfo();
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.WorkingDirectory = Environment.CurrentDirectory;
psi.FileName = FindExePath(exe); //see http://csharptest.net/?p=526
psi.Arguments = EscapeArguments(args); // see http://csharptest.net/?p=529
using (Process process = Process.Start(psi))
using (ManualResetEvent mreOut = new ManualResetEvent(false),
mreErr = new ManualResetEvent(false))
{
process.OutputDataReceived += (o, e) => { if (e.Data == null) mreOut.Set(); else output(e.Data); };
process.BeginOutputReadLine();
process.ErrorDataReceived += (o, e) => { if (e.Data == null) mreErr.Set(); else output(e.Data); };
process.BeginErrorReadLine();
string line;
while (input != null && null != (line = input.ReadLine()))
process.StandardInput.WriteLine(line);
process.StandardInput.Close();
process.WaitForExit();
mreOut.WaitOne();
mreErr.WaitOne();
return process.ExitCode;
}
}
... how can i call the function?
I modified the function with this:
public static int Run(Action<string> output, TextReader input, string exe, string args)
...because i already know the exe path and i wanto to directly pass args as direct string, but i don't know how to use the output and input variables.
By the way i understand the functionality but how to call it?
To clarify please help me to fill ? here:
Run(???, ???, "console.exe", " -someargs");
A code example will be really appreciated... and again sorry for my stupid question and for my bad english language too.
Regards
Well from what I found out,
Action<String>
can be found - What is Action<string>?
Action<String> print = (x) => Console.WriteLine(x);
List<String> names = new List<String> { "pierre", "paul", "jacques" };
names.ForEach(print);
As for TextReader, it looks like you need to read a file, which you can find out how to do - http://www.dotnetperls.com/textreader
using (TextReader reader = File.OpenText(#"C:\perl.txt"))
{
public static int Run(print, reader, "console.exe", " -someargs")
}
I can't tell you what to populate the properties of the obect with because I don't know what you're trying to achieve, but the missing parameters are basically two objects, you need to create these and pass them in. The links I provided should give you enough information on how to go about creating them.
Assuming you are not interested in the output the exe produces and you do not want to input any data into the process you can call the function like this:
Run((outMsg) => {}, null, "console.exe", " -someargs");
Explanation
The first parameter is a Action<string> which means it expects a function with one string argument. Every data that is received from the process on the standard output or standard error is passed to this function.
In my example above i just inserted a lambda expression which accepts one argument and does nothing.
The second parameter is a TextReader instance which seems to be optional and thus can be passed as null when not needed.
If set the content of the TextReader is written to the standard input of the process.
Well, i found the solution than i post it here for other people (hope i can else please delete it..)
The way to get the output is so simple:
based on this code:
public static int Run(Action<string> output, string exe, string args)
{
if (String.IsNullOrEmpty(exe))
throw new FileNotFoundException();
if (output == null)
throw new ArgumentNullException("output");
ProcessStartInfo psi = new ProcessStartInfo();
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.WorkingDirectory = Environment.CurrentDirectory;
psi.FileName = FindExePath("cdrecord.exe"); //see http://csharptest.net/?p=526
psi.Arguments = " -scanbus -v"; // see http://csharptest.net/?p=529
using (Process process = Process.Start(psi))
using (ManualResetEvent mreOut = new ManualResetEvent(false),
mreErr = new ManualResetEvent(false))
{
process.OutputDataReceived += (o, e) => { if (e.Data == null) mreOut.Set(); else output(e.Data); };
process.BeginOutputReadLine();
process.ErrorDataReceived += (o, e) => { if (e.Data == null) mreErr.Set(); else output(e.Data); };
process.BeginErrorReadLine();
output = s => ShowWindowsMessage(s);
process.StandardInput.Close();
process.WaitForExit();
mreOut.WaitOne();
mreErr.WaitOne();
return process.ExitCode;
}
}
public static string FindExePath(string exe)
{
exe = Environment.ExpandEnvironmentVariables(exe);
if (!File.Exists(exe))
{
if (Path.GetDirectoryName(exe) == String.Empty)
{
foreach (string test in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(';'))
{
string path = test.Trim();
if (!String.IsNullOrEmpty(path) && File.Exists(path = Path.Combine(path, exe)))
return Path.GetFullPath(path);
}
}
throw new FileNotFoundException(new FileNotFoundException().Message, exe);
}
return Path.GetFullPath(exe);
}
private static void ShowWindowsMessage(string message)
{
MessageBox.Show(message);
}
Which is able to start a process and read its otput, it is possible to call it with:
private void lbl_devices_Click(object sender, EventArgs e)
{
int h;
h = Run((output) => { }, "cdrecord.exe", "-scanbus -v");
}
The code i added to use the Action is:
output = s => ShowWindowsMessage(s);
Hope this should help other people like Sythnet P and Gargo helped me, thanks a lot guys!!!! smack smack
Related
i'm trying to open a process with c# and react to it, when it's been closed. This work's for me:
private void StartProc()
{
var process = new System.Diagnostics.Process { StartInfo = { FileName = "PathTo.exe" } };
process.Start();
process.EnableRaisingEvents = true;
process.Exited += this.Editor_Exited;
}
private void Editor_Exited(object sender, EventArgs e)
{
MessageBox.Show("Process canceled");
}
Lets say I'm opening a text editor with this code. If there is already an instance of this text editor the code won't open a second instance and also jumps instant in the Editor_Exited Code.
I want the code to open a new instance and don't jump in the Editor_Exited code.
string processName = "PathTo.exe";
var process = new System.Diagnostics.Process { StartInfo = { FileName = processName } };
if (process.Start())
{
process.EnableRaisingEvents = true;
process.Exited += this.Editor_Exited;
}
else
{
var p = Process.GetProcessesByName(processName);
p.WaitForExit();
}
I get this is not 100% what you are asking for, but its a work around
I am using iperf-2.0.5-2-win32 tool to find network bandwidth. I have written codes in c# which opens the cmd prompt, pass iperf parameters to start server side & client side. iperf-2.0.5-2-win32 exe will not open directly, need to open through cmd prompt only.
At present the output(Transfer rate & Bandwidth) is displaying on cmd prompt itself. I want these output to be displayed in textbox
I have tried StreamReader also. But it takes null, I have also tried OutputDataReceived Event, its also taking null.
Found few codes for ipconfig & ping.but those were not working with iperf codes.
button_click event(),
{
Process Client_proc = new Process();
ProcessStartInfo Client_command = new ProcessStartInfo("cmd.exe");
string ip = txtIP.Text;
Client_command.CreateNoWindow = true;
Client_command.WindowStyle = ProcessWindowStyle.Hidden;
Client_command.WorkingDirectory = #"E:\Iperf\RunEXE_Through_Application\iperf-2.0.5-2-win32";
Client_command.Arguments = "/c START iperf -c " + ip;
Client_proc.StartInfo = Client_command;
Client_command.RedirectStandardOutput = true;
Client_command.UseShellExecute = false;
Client_proc.OutputDataReceived += new DataReceivedEventHandler(Client_proc_OutputDataReceived);
Client_proc.Start();
Client_proc.BeginOutputReadLine();
Client_proc.WaitForExit();
}
void Client_proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => txtOutput.Text += newLine;
txtOutput.BeginInvoke(append);
}
}
Plz help me.Earlier responses are appreciated
Thanks
you use this complete code for your disposal
It is not perfect (some problems when using multiple streams )
public void RunProcess(string FileName, string Arguments, bool EventWhenExit )
{
process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.LoadUserProfile = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = FileName; // Gets or sets the application or document to start.
process.StartInfo.Arguments = Arguments;//Gets or sets the set of command-line arguments to use when starting the application
Thread.Sleep(1000);
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);/*New line */
}
process.Start();
process.BeginOutputReadLine();
PID = process.Id;
}
private void myprocess_Exited(object sender, EventArgs e)
{
process.Refresh();
Thread.Sleep(2000);
onProcessEnd(this, "ENDOF " + Proc.ToString());
Console.WriteLine("Process exsiting ");
}
private void OnDataReceivedEvent(object sender, DataReceivedEventArgs e)
{
string OutputFromProcess = e.Data;
//fire event to event handler class for further use
onDataOutputFromProcess(this, OutputFromProcess, Proc.ToString());
}
than in your GUI layer you should bind to onDataOutputFromProcess event
there you should have something like
if (screenToPrint.InvokeRequired) //&& this.Visible)
{
try
{
this.Invoke(new Action<AppendToScreenParam>(AppendTextFullConfig), new object[] { append });
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return;
}
else
{
screenToPrint.SelectionFont = font;
screenToPrint.SelectionColor = append.Color;
//screenToPrint.AppendText(append.Message);
string TextToPrint = string.Format("{0}\n", append.Message);
screenToPrint.AppendText(TextToPrint);
}
}
Maybe it is because iperf process is returning error. Subscribe the ErrorDataReceived event with Client_proc.ErrorDataReceived += Client_proc_ErrorDataReceived; and see the results. If command returns error, you can see the error message as output.
void Client_proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.txtOutput.BeginInvoke(new MethodInvoker(() => { this.txtOutput.Text = e.Data; }));
}
}
The question sounds a bit, dense. Here is a slightly longer version:
I need to have the main loop wait for user input and also have a process running and waiting for input from a stream to which the user input is to be sent.
Full story: I'm building a Cmd emulator and at first everything looked fine: The user enters a command, it gets echoed to the output area, processed and StdOut and StdErrOut are captured and also added to the output TextBox.
The only problem was, that, as the cmd process was created and started separately for each command, no state was kept. Neither variables nor codepage nor working directory etc..
So I decided to invent a little hack: Entering an opening or closing parenthesis starts and stops collecting the commands instead of executing them. After the closing parenthesis the list of commands ('batch') is used in the processBatch method to feed them all to the cmd process vie its redirected input. Worked fine.
The only problem was, obviously, now I got state but lost immediate response, so any errors wouldn't pop up until the batch was run.
So I decided to combine the good parts and, well, I knew I was heading for trouble when I realized, that to keep two loops working & waiting I have to use threading. Which I haven't done in years..
In the layout I chose the main() loop waits for user input and startCMDtask() runs startCMD() in a task. Here the input stream is scanned until is has data and then the cmd process is to process them..
But it doesn't work.
List<string> batch = new List<string>();
public volatile string output = "+";
public volatile string outputErr = "-";
Process CMD;
Task cmdTask;
volatile Queue<string> cmdQueue = new Queue<string>();
volatile public bool CMDrunning = false;
Tthis works just fine
private void processBatch()
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
foreach(string line in batch) sw.WriteLine(line);
}
output = "^"; outputErr = "~";
try { output = p.StandardOutput.ReadToEnd(); } catch { }
try { outputErr = p.StandardError.ReadToEnd(); } catch { }
try { p.WaitForExit(); } catch { }
tb_output.AppendText(output + "\r\n" + outputErr + "\r\n");
}
These don't quite, but almost..
private void setupCMD()
{
CMD = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
// info.Arguments = "/K"; // doesn't make a difference
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
CMD.StartInfo = info;
}
private void startCMDtask()
{
var task = Task.Factory.StartNew(() => startCMD());
cmdTask = task;
}
private void startCMD()
{
try { CMD.Start(); CMDrunning = true; }
catch { output = "Error starting cmd process.\r\n"; CMDrunning = false; }
using (StreamWriter sw = CMD.StandardInput)
{
if (sw.BaseStream.CanWrite)
do {
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd !="")
{
sw.WriteLine(cmd);
processOutputStreams();
}
}
catch {}
} while (CMDrunning);
}
private void processOutputStreams()
{
string newOutput = ""; string newOutputErr = "";
while (CMD.StandardOutput.Peek() > 0)
newOutput += (char)(CMD.StandardOutput.Read());
newOutput += "!?"; // at this point stdout is correctly captured (1)
try {
while (CMD.StandardError.Peek() > 0) // from here execution jumps away (2)
{ newOutputErr += (char)(CMD.StandardError.Read()); }
} catch {
newOutputErr = "?"; // never comes here
}
lock (output) // no noticable difference
lock (outputErr) //
{ // if I jump here (3) from (1) the result is displayed
// but not if i comment out the 2nd while loop (2)
if (newOutput != null & newOutput != "") output += newOutput + "\r\n";
if (newOutputErr != null & newOutputErr != "") outputErr += newOutputErr + "\r\n";
}
}
This is the call from the input processor in the main thread:
lock (cmdQueue) cmdQueue.Enqueue(cmd);
I have no idea which part is the problem: the process, the cmd shell, the input stream, the output stream, the threading, the locks or all of it in turns..??
I finally got it working. The reason for the erratic behaviour I described in the code samples was that the 3 streams were not accessed in an async'ed manner.
To rectify I discarded the processOutput function and replaced it by two calls that the process itself triggers. MS documetation gives a fine example here
I also made the StreamWriter sync, that feeds the process and the whole task it runs in as well.
Here is the new code:
private void startCMDtask()
{
var task = Task.Factory.StartNew(() => startCMD());
cmdTask = task;
}
private async void startCMD()
{
try { CMD.Start(); CMDrunning = true; }
catch { cmdErrOutput.Append("\r\nError starting cmd process.");
CMDrunning = false; }
CMD.BeginOutputReadLine();
CMD.BeginErrorReadLine();
using (StreamWriter sw = CMD.StandardInput)
{
if (sw.BaseStream.CanWrite)
do {
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd !="") await sw.WriteLineAsync(cmd);
}
catch { }
} while (CMDrunning);
try { CMD.WaitForExit(); }
catch { cmdErrOutput.Append("WaitForExit Error.\r\n"); }
}
}
This is how the process is set up now:
private void setupCMD()
{
CMD = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
CMD.OutputDataReceived += new DataReceivedEventHandler(cmdOutputDataHandler);
CMD.ErrorDataReceived += new DataReceivedEventHandler(cmdErrorDataHandler);
cmdOutput = new StringBuilder();
cmdErrOutput = new StringBuilder();
CMD.StartInfo = info;
}
And here are the output handlers:
private static void cmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{ // Add the text to the collected output.
cmdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private static void cmdErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{ // Add the text to the collected error output.
cmdErrOutput.Append(Environment.NewLine + outLine.Data);
}
}
At the end of the user input porcessing this is how the input queue is ged and the output fetched:
cmdUnDoStack.Push(cmd);
Application.DoEvents();
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => updateOutputArea(uiScheduler));
Using this little routine:
private void updateOutputArea(TaskScheduler uiScheduler)
{
Task.Factory.StartNew(() =>
{
tb_output.AppendText(cmdOutput + "\r\n" + cmdErrOutput + "\r\n");
cmdOutput.Clear();
cmdErrOutput.Clear();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
And now for the special treament some of the good old commands like CLS or COLOR need.. ;-)
I have found many examples of coding on how to execute cmd.exe and execute a command, and execute even nslookup and interact, but the problem I am having is with a particular dos program that when it starts, it does not stop "outputting". here is some code and I will put a comment and the errors I get from C#
Here is how I have it setup in a more advanced way so I can receive output from the program on events
public void StartApplication(string appNameAndPath)
{
StreamReader outputStream;
Process p = new Process();
p.StartInfo.FileName = appNameAndPath;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = false;//for now just so I can see it
p.Start();
//here is my advanced example
if(advanced == true)
{
outputStream = p.StandardOutput;
DoReadOutPut();
}
else
{//here is a simple example
while (p.StandardOutput.ReadLine() != null) //this hangs here until the application exists
{
txt += (p.StandardOutput.ReadLine());
}
}
}
void DoReadOutput()
{
outputStream.BaseStream.BeginRead( readOutputBuffer, 0, readOutputBuffer.Length, new AsyncCallback( OnReadOutputCompleted ), null );
//this does sometimes fire but only with 0 bytes, on other dos programs it would say Memory read not allowed
}
void OnReadOutputCompleted( IAsyncResult result )
{
int cbRead = outputStream.BaseStream.EndRead( result );
ProcessOutput( readOutputBuffer, cbRead );
DoReadOutput();
}
private void ProcessOutput(byte[] buffer, int cbRead)
{
string text = p.StartInfo.StandardOutputEncoding.GetString(buffer, 0, 10000); //this is where it hangs until the program exits or is not writing anymore
this.Invoke((Action)delegate
{
SetTextBoxValue(text);//im doing this because im on another thread otherwise textBox1.Text - text"
});
}
I do not want to have to use API and GetText and create an engine to ReadLastLine, can anyone help me with this? I suppose you would want an example exe, creating a C# application that while(true){Console.WriteLine("bla");} would suffice as the example exe but not the exe I am having trouble with. The exe takes over the dos window and has an "old school interface"
async/await can help here....
await Exec(yourExe,parameters);
Task Exec(string exe,string args)
{
var tcs = new TaskCompletionSource<object>();
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = exe;
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.Arguments = args;
var proc = Process.Start(psi);
proc.OutputDataReceived += (s, e) =>
{
this.Invoke((Action) (()=>richTextBox1.AppendText(e.Data + Environment.NewLine)));
};
proc.Exited += (s, e) => tcs.SetResult(null);
proc.EnableRaisingEvents = true;
proc.BeginOutputReadLine();
return tcs.Task;
}
You need to handle callback events to read streams:
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
Process proc = new Process();
proc.StartInfo = startInfo;
proc.ErrorDataReceived += new DataReceivedEventHandler(DataReceiveHandler);
proc.OutputDataReceived += new DataReceivedEventHandler(DataReceiveHandler);
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
Code borrowed from this post
How do I invoke a console application from my .NET application and capture all the output generated in the console?
(Remember, I don't want to save the information first in a file and then relist as I would love to receive it as live.)
This can be quite easily achieved using the ProcessStartInfo.RedirectStandardOutput property. A full sample is contained in the linked MSDN documentation; the only caveat is that you may have to redirect the standard error stream as well to see all output of your application.
Process compiler = new Process();
compiler.StartInfo.FileName = "csc.exe";
compiler.StartInfo.Arguments = "/r:System.dll /out:sample.exe stdstr.cs";
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.RedirectStandardOutput = true;
compiler.Start();
Console.WriteLine(compiler.StandardOutput.ReadToEnd());
compiler.WaitForExit();
This is bit improvement over accepted answer from #mdb. Specifically, we also capture error output of the process. Additionally, we capture these outputs through events because ReadToEnd() doesn't work if you want to capture both error and regular output. It took me while to make this work because it actually also requires BeginxxxReadLine() calls after Start().
Asynchronous way:
using System.Diagnostics;
Process process = new Process();
void LaunchProcess()
{
process.EnableRaisingEvents = true;
process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
process.Exited += new System.EventHandler(process_Exited);
process.StartInfo.FileName = "some.exe";
process.StartInfo.Arguments = "param1 param2";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
//below line is optional if we want a blocking call
//process.WaitForExit();
}
void process_Exited(object sender, EventArgs e)
{
Console.WriteLine(string.Format("process exited with code {0}\n", process.ExitCode.ToString()));
}
void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data + "\n");
}
void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data + "\n");
}
Use ProcessStartInfo.RedirectStandardOutput to redirect the output when creating your console process.
Then you can use Process.StandardOutput to read the program output.
The second link has a sample code how to do it.
ConsoleAppLauncher is an open source library made specifically to answer that question. It captures all the output generated in the console and provides simple interface to start and close console application.
The ConsoleOutput event is fired every time when a new line is written by the console to standard/error output. The lines are queued and guaranteed to follow the output order.
Also available as NuGet package.
Sample call to get full console output:
// Run simplest shell command and return its output.
public static string GetWindowsVersion()
{
return ConsoleApp.Run("cmd", "/c ver").Output.Trim();
}
Sample with live feedback:
// Run ping.exe asynchronously and return roundtrip times back to the caller in a callback
public static void PingUrl(string url, Action<string> replyHandler)
{
var regex = new Regex("(time=|Average = )(?<time>.*?ms)", RegexOptions.Compiled);
var app = new ConsoleApp("ping", url);
app.ConsoleOutput += (o, args) =>
{
var match = regex.Match(args.Line);
if (match.Success)
{
var roundtripTime = match.Groups["time"].Value;
replyHandler(roundtripTime);
}
};
app.Run();
}
I've added a number of helper methods to the O2 Platform (Open Source project) which allow you easily script an interaction with another process via the console output and input (see http://code.google.com/p/o2platform/source/browse/trunk/O2_Scripts/APIs/Windows/CmdExe/CmdExeAPI.cs)
Also useful for you might be the API that allows the viewing of the console output of the current process (in an existing control or popup window). See this blog post for more details: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (this blog also contains details of how to consume the console output of new processes)
I made a reactive version that accepts callbacks for stdOut and StdErr.
onStdOut and onStdErr are called asynchronously,
as soon as data arrives (before the process exits).
public static Int32 RunProcess(String path,
String args,
Action<String> onStdOut = null,
Action<String> onStdErr = null)
{
var readStdOut = onStdOut != null;
var readStdErr = onStdErr != null;
var process = new Process
{
StartInfo =
{
FileName = path,
Arguments = args,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = readStdOut,
RedirectStandardError = readStdErr,
}
};
process.Start();
if (readStdOut) Task.Run(() => ReadStream(process.StandardOutput, onStdOut));
if (readStdErr) Task.Run(() => ReadStream(process.StandardError, onStdErr));
process.WaitForExit();
return process.ExitCode;
}
private static void ReadStream(TextReader textReader, Action<String> callback)
{
while (true)
{
var line = textReader.ReadLine();
if (line == null)
break;
callback(line);
}
}
Example usage
The following will run executable with args and print
stdOut in white
stdErr in red
to the console.
RunProcess(
executable,
args,
s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
s => { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(s); }
);
From PythonTR - Python Programcıları Derneği, e-kitap, örnek:
Process p = new Process(); // Create new object
p.StartInfo.UseShellExecute = false; // Do not use shell
p.StartInfo.RedirectStandardOutput = true; // Redirect output
p.StartInfo.FileName = "c:\\python26\\python.exe"; // Path of our Python compiler
p.StartInfo.Arguments = "c:\\python26\\Hello_C_Python.py"; // Path of the .py to be executed
Added process.StartInfo.**CreateNoWindow** = true; and timeout.
private static void CaptureConsoleAppOutput(string exeName, string arguments, int timeoutMilliseconds, out int exitCode, out string output)
{
using (Process process = new Process())
{
process.StartInfo.FileName = exeName;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
output = process.StandardOutput.ReadToEnd();
bool exited = process.WaitForExit(timeoutMilliseconds);
if (exited)
{
exitCode = process.ExitCode;
}
else
{
exitCode = -1;
}
}
}