Process output redirection batch stopps c# - c#

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();
}

Related

C# Prevent a started app from writing in the console

I have an app which starts another app. This other app prints a few lines into the Console but noone needs this output and it prints it's output betwenn my own. How can I prevent this other app from printing it's stuff into my console?
I tried to run with ProcessStartInfo.UseShellExecutive both on true and false, also tried to change the console output into a MemoryStream before starting but since I need the Console i had to change the output back and it looks like the other app got their input changed back too.
Process serverprocess = new Process();
serverprocess.StartInfo.FileName = Path.GetFileName(serverpath);
serverprocess.StartInfo.Arguments = launch;
serverprocess.StartInfo.UseShellExecute = false;
serverprocess.StartInfo.RedirectStandardOutput = true;
serverprocess.Start();
In your code ensure that you are re-directing both StandardOutput and StandardError that way everything that the "ThirdPartyApp" writes will be captured in either of these streams.
I have written a small Helper Class that helps with this
You can use like
//Launching excel.exe with /safe as arg
var excelExample1 = #"""C:\Program Files (x86)\Microsoft Office\Office15\EXCEL.EXE"" /safe";
LaunchCMD.Invoke(excelExample1);
//To get its output, if any
var getOutput = LaunchCMD.Output;
LaunchCMD Helper Class
class LaunchCMD
{
public static string Output
{
get; set;
} = "";
public static void Invoke(string command, bool waitTillExit = false, bool closeOutputWindow = false)
{
ProcessStartInfo ProcessInfo;
Process Process = new Process();
ProcessInfo = new ProcessStartInfo("cmd.exe", "/C " + command);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = false;
ProcessInfo.RedirectStandardOutput = true;
ProcessInfo.RedirectStandardError = true;
Process.EnableRaisingEvents = true;
Process = Process.Start(ProcessInfo);
Process.ErrorDataReceived += ConsoleDataReceived;
Process.OutputDataReceived += ConsoleDataReceived;
Process.BeginOutputReadLine();
Process.BeginErrorReadLine();
if (waitTillExit == true)
{
Process.WaitForExit();
}
if (closeOutputWindow == true)
{
Process.CloseMainWindow();
}
Process.Close();
System.Threading.Thread.Sleep(1000);
Output.ToString();
}
private static void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(1000);
if (e.Data != null)
{
Output = Output + e.Data;
}
}
}

Accessing forms control from function handler on C#

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

How to read and write to a command prompt process (cmd.exe) with WPF

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.

Getting data from Process.StandardOutput on the fly

I am trying to get data from Process.StandardOutput ... but I have a problem : i get the data when the process is ended, but not during execution (does it not flush ???) . It looks like the data is bufferred somewhere.
When I run the process manually the messages apper during execution. How to fix?
this is what I use to grab output from a process. This is adding to a stringbuilder, but you could do other things.
private void RunWithOutput(string exe, string parameters, out string result, out int exitCode)
{
ProcessStartInfo startInfo = new ProcessStartInfo(exe, parameters);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
Process p = new Process();
p.StartInfo = startInfo;
p.Start();
StringBuilder sb = new StringBuilder();
object locker = new object();
p.OutputDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs args)
{
lock(locker)
{
sb.Append(args.Data);
}
} );
p.ErrorDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs args)
{
lock (locker)
{
sb.Append(args.Data);
}
});
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.WaitForExit();
result = sb.ToString();
exitCode = p.ExitCode;
}

RedirectStandard Output/Error wont call my event for Standard Output/Error

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.

Categories