I have created an interactive shell on C# but I don't know how to access my forms control and assign received values to my textbox, I know that the threads can't access the UI thread but in this case, I don't seem to be able to fix the problem, there is going to be many input and output in that shell, and I want to make sure that everything is shown to user.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Process process = new Process();
process.StartInfo.FileName = "echoo.exe";
process.StartInfo.Arguments = "";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.StartInfo.RedirectStandardInput = true;
process.Start();
StreamWriter sw = process.StandardInput;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
sw.WriteLine("sent");
process.WaitForExit();
}
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
Form1.textBox1.Text = outLine.Data;
}
}
If you write this in your setter:
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if(textBox1.InvokeRequired)
{
textBox1.BeginInvoke((MethodInvoker) delegate() {textBox1.Text = outLine.Data;});
}
else
{
textBox1.Text = outLine.Data;
}
}
It'll force the set onto the UI thread. I found this from this question: stack question
Related
I'm new at c# / winforms and try to batch convert video clips with handbrake. The convert itself is working when the processes are opened in an own windows without redirecting the Stdout/Stderr.
But when I redirect the output to a winforms textbox only the first clip is converted. As I can see in the taskmanager the handbrake_Cli is already opened but doing nothing.
I think that there is some STDerr/STDout in any buffer and is waiting to get flushed.... but I don't know how to do. Would be glad if anybody can give me a hint :-)
ProcessStartInfo info = new ProcessStartInfo(" \"" + textBoxHandbrakeCLI.Text + "\"");
process.StartInfo = info;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.Exited += new EventHandler(process_Exited);
process.EnableRaisingEvents = true;
eventHandled = new TaskCompletionSource<bool>();
//Redirect output
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.OutputDataReceived += (sender, eventArgs) => MyProcOutputHandler(sender, eventArgs);
process.ErrorDataReceived += (sender, eventArgs) => MyProcOutputHandler(sender, eventArgs);
foreach(pseudo)
{
process.Start();
if (!errorRedirect)
{
process.BeginErrorReadLine();
process.BeginOutputReadLine();
errorRedirect = true;
}
}
private void MyProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(outLine.Data);
WriteStatus(sb.ToString());
}
public void WriteStatus(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(WriteStatus), new object[] { value});
return;
}
try
{
textBoxAdvancedStatus.AppendText(Environment.NewLine + value);
}
catch
{
//stay calm
}
}
I got a workaround for my problem but still donĀ“t know why i need it.
foreach(pseudo) {
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelErrorRead();
process.CancelOutputRead();
}
This is probably simple, and the question might not be very good, but I'm looking for the best or most efficient way to accomplish this:
A button click starts an Event, which then runs a method that continiously pings an IP address. The ping output is displayed in a text box.
A click on the same button stops the ping task.
Here's the (I think) relevant code:
The method run by the Event connected to the Ping button:
private void pingClicked (object sender, EventArgs e) {
pinger();
}
The pinger() method:
private void pinger() {
string command = "/c ping " + ipadrtextbox.Text;
if (contchkbox.Checked) {
command += " -t";
}
ProcessStartInfo procStartInfo = new ProcessStartInfo("CMD", command);
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.Start();
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
proc.OutputDataReceived += new
DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.BeginOutputReadLine();
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e) {
if (e.Data != null) {
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => pingoutput.AppendText(newLine);
pingoutput.BeginInvoke(append);
}
}
A while-loop in the pinger method results in a complaint that "An async read operation has already been started on the stream.", so that's apparently not the way to go.
Also, I haven't found a way for the method to listen for a buttonpress elsewhere in the application, and then stop the task with roc.CancelOutputRead(). But I expect that's the way the task should be stopped?
Keep the running process as a private member of your class then:
private void pingClicked (object sender, EventArgs e) {
if( process != null && !process.HasExited )
{
process.CancelOutputRead()
process.Kill();
process=null;
} else {
pinger();
}
}
I've been looking around google and SO for many hours now as I feel this question must have been asked, but I cannot find the right answers.
Many of the most similar questions ask how to send A command or series of them, at start up of the process. The difference I'm looking for help with is being able to continually send and receive commands to the process once I've started it. Essentially nesting an actual command prompt inside a WPF window.
This is how I've been trying to run the process. But I can't figure out how tell the process to wait for a input response.
public void startProcess()
{
using (Process process = new Process())
{
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WorkingDirectory = "C:\\";
process.StartInfo.FileName = #"cmd.exe"; //
process.OutputDataReceived += ProcessOutputDataHandler;
process.ErrorDataReceived += ProcessErrorDataHandler;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
using (sWriter = process.StandardInput)
{
if (sWriter.BaseStream.CanWrite)
{
sWriter.WriteLine(#"cd C:\Users\username\Desktop\SomeFolder\ ");
sWriter.WriteLine(#"Run_Script.pl");
}
}
process.WaitForExit();
}
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
// this method won't work, but this is the general idea I'm trying to figure out to implement.
if (sWriter.BaseStream.CanWrite)
{
sWriter.WriteLine(txtInput.Text);
}
}
public void ProcessOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
// log is a output textbox that is updated on a timer
txtLog.Dispatcher.BeginInvoke(new Action(() => { log += outLine.Data; }), null);
}
public void ProcessErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
txtLog.Dispatcher.BeginInvoke(new Action(() => { log += outLine.Data; }), null);
}
I tagged perl on this just because I'm trying to run a perl script with this.
I have a Process that needs to update the console in realtime based on the output. But its not working. The console just opens and closes, and the Process runs in the background. I can not figure out what I am doing wrong. Here is my code:
private static StringBuilder sortOutput = null;
static void Main(string[] args)
{
Process process;
process = new Process();
process.StartInfo.FileName = "C:\\ffmbc\\ffmbc.exe";
//process.StartInfo.Arguments = "-i new5830df.mxf -an ";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
sortOutput = new StringBuilder("");
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.Exited += new EventHandler(myProcess_Exited);
process.StartInfo.RedirectStandardInput = true;
process.Start();
process.BeginOutputReadLine();
}
private static void OutputHandler(object sender, DataReceivedEventArgs outLine)
{
string line;
line = (outLine.Data.ToString());
Console.WriteLine(line);
}
private static void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine("Proccess Finished");
}
Make sure to call process.WaitForExit() to block until the process exits.
Your Main() has exited, so the console closes. You need to wait until the called program completed, before you exit your Main function.
hi i'm trying to build a parser for my System to Manage my Tekkit Server i am using C# but i have RedirectStandardOutput on my Tekkit Server process and there is a method set-up to then send that output to my console after adding to a List but it's not adding to a List<string>
Here is my code:
public void StartServer(string maxMem, string minMem, string path)
{
ThreadStart server = new ThreadStart(delegate() { StartServerThread(maxMem, minMem, path); });
server.Invoke();
}
private void StartServerThread(string maxMem, string minMem, string TekkitPath)
{
try
{
TekkitServer.StartInfo.FileName = "java";
TekkitServer.StartInfo.Arguments = String.Format("-Xmx{0} -Xms{1} -jar \"" + TekkitPath + "\" -nojline nogui", maxMem, minMem);
TekkitServer.StartInfo.UseShellExecute = false;
TekkitServer.StartInfo.RedirectStandardInput = true;
TekkitServer.StartInfo.RedirectStandardOutput = true;
TekkitServer.OutputDataReceived += new DataReceivedEventHandler(TekkitServer_OutputDataReceived);
IsStarted = TekkitServer.Start();
TekkitServerInput = TekkitServer.StandardInput;
}
catch (Exception)
{
}
}
void TekkitServer_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
/*B*/recordedData.Add(e.Data);
Console.Out.WriteLine(e.Data);
}
Where /*B*/ is a break point, the breakpoint is never activating
By default, the standard output is directed at the console window.
If you need to do something with it, you need to redirect it, hence, you need to set RedirectStandardOutput = true; for the event to be fired.
Edit: This is my working code (with error handling and logging omitted):
public int ExecuteCommand(CommandParameters parameters)
{
Process process = new Process();
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += StdOutputHandler;
process.ErrorDataReceived += StdErrorHandler;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = ...;
process.StartInfo.Arguments = ...;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit(parameters.Timeout);
return process.ExitCode;
}
private void StdOutputHandler(object sendingProcess, DataReceivedEventArgs outdata)
{
if (!string.IsNullOrEmpty(outdata.Data))
{
OutputMessages.Add(outdata.Data);
}
}
Most likely the missing link in your code is the BeginOutputReadLine that actually gets the handler method on it's way.
Also, I use a fresh Process object and that I wait on it to finish it's job, so no interference with previous calls is possible.