How would you execute a batch script within a GUI form in C#
Could anyone provide a sample please?
System.Diagnotics.Process.Start("yourbatch.bat"); ought to do it.
Another thread covering the same issue.
This example assumes a Windows Forms application with two text boxes (RunResults and Errors).
// Remember to also add a using System.Diagnostics at the top of the class
private void RunIt_Click(object sender, EventArgs e)
{
using (Process p = new Process())
{
p.StartInfo.WorkingDirectory = "<path to batch file folder>";
p.StartInfo.FileName = "<path to batch file itself>";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
p.WaitForExit();
// Capture output from batch file written to stdout and put in the
// RunResults textbox
string output = p.StandardOutput.ReadToEnd();
if (!String.IsNullOrEmpty(output) && output.Trim() != "")
{
this.RunResults.Text = output;
}
// Capture any errors written to stderr and put in the errors textbox.
string errors = p.StandardError.ReadToEnd();
if (!String.IsNullOrEmpty(errors) & errors.Trim() != ""))
{
this.Errors.Text = errors;
}
}
}
Updated:
The sample above is a button click event for a button called RunIt. There's a couple of text boxes on the form, RunResults and Errors where we write the results of stdout and stderr to.
I deduce that by executing within a GUI form you mean showing execution results within some UI-Control.
Maybe something like this:
private void runSyncAndGetResults_Click(object sender, System.EventArgs e)
{
System.Diagnostics.ProcessStartInfo psi =
new System.Diagnostics.ProcessStartInfo(#"C:\batch.bat");
psi.RedirectStandardOutput = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
System.Diagnostics.Process batchProcess;
batchProcess = System.Diagnostics.Process.Start(psi);
System.IO.StreamReader myOutput = batchProcess.StandardOutput;
batchProcess.WaitForExit(2000);
if (batchProcess.HasExited)
{
string output = myOutput.ReadToEnd();
// Print 'output' string to UI-control
}
}
Example taken from here.
Related
I've got a tricky issue with a console app, from which I'm trying to redirect StandardInput, StandardOutput and StandardError.
I've got a working solution for other console app - that's not a new thing for me. But this app seems to have something special, which is making impossible to redirect all outputs with a standard approach.
Console app works like this:
directly after startup writes a few lines and waits for user input
no matter what input is - console app is showing some output and again wait for new input
console app never ends, it has to be closed
I've tried already solutions based on:
StandardOutput/Error.ReadToEnd()
taki care of OutputDataReceived & ErrorDataReceived with read line by line with ReadLine
reading by char
waiting for the end of process (which is not ending, so I'm running into a deadlock)
to start console app in a preloaded cmd.exe and grab this (StandardOutput stopped to show just after launch of this console app)
to manually flush input
All the time I had completely no output and no error stream from console app - nothing.
After a multitude attempts I've discovered, that I can receive StandardOutput only when I'll close StandardInput after programatically inputting the data.
But in this case, console app is going wild, falling into loop of writing few lines to StandardOutput as on start-up, which makes final output big and full of garbages.
With MedallionShell library I'm able to try to gracefully kill it with Ctrl+C, but this solution is still far from acceptable, because:
sometimes console app will produce so much garbages before I will be able to kill it, that it crashes my app
even if this won't crash, searching for expected output in a lot of garbages is nasty and tragically slows down automatization (6000 records in... 15 minutes)
I'm unable to provide more than one input at a time, so I have to start console app just to receive one output, close and start again for another output
I've got no sources for that console app, so I'm even not able to recreate the issue or fix it - it's some very legacy app at my company, which I'm trying to make at least a bit automatic...
Code, with which I've got at least anything now (with MediallionShell):
var command = Command.Run(Environment.CurrentDirectory + #"\console_app.exe");
command.StandardInput.WriteLine("expected_input");
command.StandardInput.Close(); // without that I'll never receive any output or error stream from this stubborn console app
command.TrySignalAsync(CommandSignal.ControlC); // attempt to kill gracefully, not always successfull...
var result = command.Result;
textBox1.AppendText(String.Join(Environment.NewLine, command.GetOutputAndErrorLines().ToArray().Take(10))); // trying to get rid of garbages
command.Kill(); // additional kill attempt if Ctrl+C didn't help, sometimes even this fails
Any help will be appreciated, I'm also still searching for solution and now I'm checking this one: PostMessage not working with console window whose output is redirected and read asynchronously but author there had an output and I don't...
You haven't provided a sample Console program to test with, but something like the following may work:
Create a Console project (Console (.NET Framework)) - used for testing
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleTestApp
{
class Program
{
static void Main(string[] args)
{
//prompt for input - 1st prompt
Console.Write("Please enter filename: ");
string filename = Console.ReadLine();
if (System.IO.File.Exists(filename))
{
Console.WriteLine("'" + filename + "' exists.");
}
else
{
Console.Error.WriteLine("'" + filename + "' doesn't exist.");
}
//prompt for input - 2nd prompt
Console.Write("Would you like to exit? ");
string answer = Console.ReadLine();
Console.WriteLine("Your answer was: " + answer);
Console.WriteLine("Operation complete.");
}
}
}
Then, create a Windows Forms project Windows Forms (.NET Framework) and run one of the following:
Option 1:
private void RunCmd(string exePath, string arguments = null)
{
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(exePath, arguments);
startInfo.Arguments = arguments; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
}
};
p.Start(); //start
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
using (StreamWriter sw = p.StandardInput)
{
//provide values for each input prompt
//ToDo: add values for each input prompt - changing the for loop as necessary
for (int i = 0; i < 2; i++)
{
if (i == 0)
sw.WriteLine(#"C:\Temp\Test1.txt"); //1st prompt
else if (i == 1)
sw.WriteLine("Yes"); //2nd prompt
else
break; //exit
}
}
//waits until the process is finished before continuing
p.WaitForExit();
}
}
Option 2:
private void RunCmd(string exePath, string arguments = null)
{
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(exePath, arguments);
startInfo.Arguments = arguments; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
}
};
p.Start(); //start
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
using (StreamWriter sw = p.StandardInput)
{
//provide values for each input prompt
//ToDo: add values for each input prompt - changing the for loop as necessary
sw.WriteLine(#"C:\Temp\Test1.txt"); //1st prompt
sw.WriteLine("Yes"); //2nd prompt
}
//waits until the process is finished before continuing
p.WaitForExit();
}
}
Option 3:
Note: This one is modified from here.
private void RunCmd(string exePath, string arguments = null)
{
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(exePath, arguments);
startInfo.Arguments = arguments; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
p.Start(); //start
Read(p.StandardOutput);
Read(p.StandardError);
using (StreamWriter sw = p.StandardInput)
{
//provide values for each input prompt
//ToDo: add values for each input prompt - changing the for loop as necessary
sw.WriteLine(#"C:\Temp\Test1.txt"); //1st prompt
sw.WriteLine("Yes"); //2nd prompt
}
//waits until the process is finished before continuing
p.WaitForExit();
}
}
private static void Read(StreamReader reader)
{
new System.Threading.Thread(() =>
{
while (true)
{
int current;
while ((current = reader.Read()) >= 0)
Console.Write((char)current);
}
}).Start();
}
I have following Python script :
import time
for x in range(10):
print(x)
time.sleep(0.05)
I want to run this script and capture its output while it is running.
I used the following C# code but it does not print any number until it completely finishes loop.
private void DoScriptTest()
{
ProcessStartInfo start = new ProcessStartInfo();
string cmd = #"c:\flowers\count.py";
start.FileName = #"C:\Users\pubud\AppData\Local\Programs\Python\Python36\python.exe";
start.Arguments = string.Format("{0}", cmd);
start.UseShellExecute = false;// Do not use OS shell
start.CreateNoWindow = true; // We don't need new window
start.RedirectStandardOutput = true;// Any output, generated by application will be redirected back
start.RedirectStandardError = true; // Any error in standard output will be redirected back (for example exceptions)
Process process = new Process();
process.StartInfo = start;
process.EnableRaisingEvents = true;
process.OutputDataReceived += ProcessOutputHandler;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
}
private void ProcessOutputHandler(object sender, DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
try {
TxtPrompt.Invoke((MethodInvoker)delegate {
TxtPrompt.AppendText(e.Data);
TxtPrompt.Refresh();
});
}
catch
{ }
}
I am not sure why ProcessOutputHandler is NOT called in while the script is running. How could I get that output ? (the numbers coming from Python script in real time)
I want to launch a .exe program using C# and read the values from the cmd generated from the .exe
The .exe launches successfully but I cannot read the values:
This is my code:
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = (#"D:\BSC\Thesis\Raphael_Thesis\smiledetector\bin\smiledetector.exe");
start.WorkingDirectory = #"D:\BSC\Thesis\Raphael_Thesis\smiledetector\bin\";
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
//Start the process
using (Process process = Process.Start(start))
{
// Read in all the text from the process with the StreamReader.
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
textBox1.Text = result;
}
}
As Sam I Am has indicated, drop the using block for the StreamReader
using (Process process = Process.Start(start))
{
string result = process.StandardOutput.ReadToEnd();
textBox1.Text = result;
}
Keep in mind however your calling application will block until process completes and all the output can be read.
You need something like this:
private void btnStart_Click(object sender, EventArgs e)
{
p.StartInfo.FileName = #"D:\BSC\Thesis\Raphael_Thesis\smileDetector\vs2010\smiledetectorDebug";
p.StartInfo.WorkingDirectory = #"D:\BSC\Thesis\Raphael_Thesis\smileDetector\vs2010\";
p.StartInfo.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.StartInfo.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputHander);
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
}
where p is Process p = new Process();
void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
data.Add(Convert.ToInt32(e.Data));
}
where data is a List of int
I don't see any weird thing in your code, it should work fine. Taking a look at the doc, it gives a brief example of how to redirect it:
// Start the child process.
using(Process p = new Process())
{
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"D:\BSC\Thesis\Raphael_Thesis\smiledetector\bin\smiledetector.exe";
p.WorkingDirectory = #"D:\BSC\Thesis\Raphael_Thesis\smiledetector\bin\";
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();
}
source: Process.StandardOutput Property
This is windows-application-form code; I want the batch file which is going to be executed to show the output on shell screen which I got by RedirectStandardOutput = false;, but I also want output to be redirected to a log file at the same time. For this, I use RedirectStandardOutput = true;.
Of course, only one can be used at one time!
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "c:\test\build.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true; // if I use false all the commented lines below are not applicable for comments
p.Start();
string output = null;
//try
//{
output = p.StandardOutput.ReadToEnd();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.ToString());
//}
System.IO.File.WriteAllText("c:\test\log.txt", output);
Capture the output and print it to the screen yourself. That's how the tee command meets this need on most non-Windows operating systems.
You could try something like this:
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "c:\test\build.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
p.Start();
And have an event handler somewhere in your code:
private static void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
Console.WriteLine(outLine.Data);
System.IO.File.AppendAllText("c:\test\log.txt", outLine.Data);
}
}
Example taken from MSDN: Process.BeginOutputReadLine Method. It would be more efficient to keep the file open for writing, or even to buffer it but this keeps the example short.
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;
}
}
}