Read and Write commands into a single CMD process - c#

Hi I want to start a single cmd process from which i can read and write from my windows application form. I want to highlight that i dont want to keep on create a new cmd process for each command that i enter. My code is a mess now. . .
I have a windows form application now with just 2 textboxes and a button.
Textbox1 is for the output.
Textbox2 in for the input.
Button is to send the input.
Process cmd = new Process();
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName = "cmd.exe",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardInput = true
};
StreamReader sr;
StreamWriter sw;
private void Form1_Load(object sender, EventArgs e)
{
cmd.StartInfo = psi;
cmd.Start();
sr = cmd.StandardOutput;
sw = cmd.StandardInput;
sw.AutoFlush = true;
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(GetOutput));
t.Start();
}
delegate void Write(string Text);
private void SetTextBox(string Text)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke(new Write(SetTextBox),Text);
}
else
textBox1.Text += Text;
}
private void GetOutput()
{
while (true)
{
if (sr.Peek() != -1)
SetTextBox(sr.ReadToEnd());
else
System.Threading.Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
sw.WriteLine(textBox2.Text);
textBox2.Clear();
}

Try using the /k argument, which tells CMD.exe to open, run the specified command, then keep the window open.

Related

Create and actively use Process without freezing main thread

I'm currently trying to create new NodeJS process and while it's running, put it's console output into my winform textbox.
Whenever this process is executed, it's freezing main thread as if form is waiting for this process to exit. After the process is closed thats when the console output is added to the textbox.
What I'm trying to achieve is simultaneously have this node process running in the background and have whatever it's outputing in the textbox.
Edit 1:
I Managed to run the console without freezing main thread but the output only shows when the process is closed
My current code:
private void Btn_connect_Click(object sender, EventArgs e)
{
if(backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
nodeProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "node.exe";
startInfo.Arguments = #"path" + " arg1 arg2 arg3";
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
nodeProcess.StartInfo = startInfo;
nodeProcess.Start();
while (worker.CancellationPending != true)
{
Thread.Sleep(200);
AddText(nodeProcess.StandardOutput.ReadToEnd());
worker.ReportProgress(1);
}
e.Cancel = true;
}
public void AddText(string text)
{
if(txt_log.InvokeRequired)
{
txt_log.Invoke(new Action<string>(AddText), new object[] { text });
return;
}
txt_log.Text += "\n " + text;
}
Instead of a BackgroundWorker you could try using Process.BeginOutputReadLine, Process.OutputDataReceived, and Process.Exited.
void StartProcess()
{
Process nodeProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "node.exe";
startInfo.Arguments = #"path" + " arg1 arg2 arg3";
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
nodeProcess.StartInfo = startInfo;
nodeProcess.EnableRaisingEvents = true;
nodeProcess.Exited += nodeProcess_Exited;
nodeProcess.OutputDataReceived += nodeProcess_OutputDataReceived;
nodeProcess.Start();
nodeProcess.BeginOutputReadLine();
}
void nodeProcess_Exited(object sender, EventArgs e)
{
// Do something when the process exits, if you need to.
// You'll want to check InvokeRequired before you modify any of your form's controls.
}
void nodeProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (txt_log.InvokeRequired)
{
txt_log.Invoke(new Action<string>(AddText), new object[] { e.Data });
return;
}
txt_log.Text += "\n " + e.Data;
}

Duplicate process start doesn't react as expected

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

How to display output of iperf cmd prompt in textbox

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

Running batch file

I have been working on a manager application for a Minecraft server, when I run my program, the console shows and disappears, if I run it manually, it runs without and problems. My code:
Process process = new Process
{
StartInfo =
{
FileName = textBox2.Text,
//Arguments = textBox3.Text,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = false,
}
};
process.OutputDataReceived += new DataReceivedEventHandler(server_outputDataReceived);
server = process;
process.Start();
Batch file code (idk what language are batch files, so I used default one - Select Language):
java -Xmx1024M -jar craftbukkit-1.7.2-R0.3.jar -o false
BTW. Can you start processes without creating files ? (ex. Start process "java -jar example", without creating file) ?
#Edit Answer to the third question: Answer to the third question
My full code (MessageBoxes are in Polish, becouse im from Poland, but later i will add support for other languages):
using System;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Process server;
private Boolean runServer()
{
if (!File.Exists(textBox2.Text))
{
MessageBox.Show("Brak określonej ścieżki dostępu! (" + textBox2.Text + ")", "Błąd", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
Process process = new Process
{
StartInfo =
{
FileName = textBox2.Text,
//Arguments = textBox3.Text,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = false,
}
};
process.OutputDataReceived += new DataReceivedEventHandler(server_outputDataReceived);
process.ErrorDataReceived += new DataReceivedEventHandler(server_outputDataReceived);
server = process;
if (process.Start())
return true;
else
{
MessageBox.Show("Nie można włączyć serwera!", "Błąd", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private String ReadFile(String filename, int line)
{
StreamReader reader = new StreamReader(filename);
for (int i = 0; i < line; i++)
{
reader.ReadLine();
}
return reader.ReadLine();
}
private void ReloadOPs()
{
if (!File.Exists(textBox1.Text))
{
MessageBox.Show("Sciezka dostępu do pliku z listą graczy posiadających OP nie istnieje! (" + textBox1.Text + ")", "Błąd", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
tabControl1.SelectedTab = tabPageOptions;
textBox1.SelectAll();
return;
}
String line = ReadFile(textBox1.Text, 0);
comboBox1.Items.Clear();
for (int i = 1; i < File.ReadAllLines(textBox1.Text).Length; i++)
{
if (!String.IsNullOrWhiteSpace(ReadFile(textBox1.Text, i)))
{
comboBox1.Items.Add(line);
line = ReadFile(textBox1.Text, i);
}
}
MessageBox.Show("Lista graczy z OP, została odświeżona.");
}
// OPs combobox (OPs)
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
groupBox1.Text = comboBox1.SelectedItem.ToString();
groupBox1.Visible = true;
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Text = Application.StartupPath.ToString() + #"\ops.txt";
ReloadOPs();
}
// Reload OPs button (OPs)
private void button1_Click(object sender, EventArgs e)
{
ReloadOPs();
}
// Save button (Options)
private void button4_Click(object sender, EventArgs e)
{
}
private void server_outputDataReceived(object sender, DataReceivedEventArgs e)
{
addConsoleMessage(e.Data.ToString(), true);
}
// Run server button (Menu)
private void button5_Click(object sender, EventArgs e)
{
if (!runServer())
return;
server.BeginOutputReadLine();
button6.Enabled = true;
}
// Stop server button (Menu)
private void button6_Click(object sender, EventArgs e)
{
if(!server.HasExited)
server.Kill();
button6.Enabled = false;
}
private void addConsoleMessage(String message, Boolean refresh)
{
listBox1.Items.Add(message);
if (refresh)
listBox1.Refresh();
}
}
}
Works now, but after second message that batch file returned (?), program crashes becouse InvaildOperationException was unhandled.
Ok. Let's clarify some points. I assume you have a Batch file named filename.bat with this content:
java -Xmx1024M -jar craftbukkit-1.7.2-R0.3.jar -o false
and with "run it manually" you mean you open a command-line window and enter: filename. Ok? However, you have NOT indicated how do you run your Batch file when NOT run it manually! If you double-click on it from the file browser, then your Batch file missing to change current directory to the one where the Batch file is:
cd "%~P0"
java -Xmx1024M -jar craftbukkit-1.7.2-R0.3.jar -o false
PS - Your Batch file should NOT be named start.bat because start is the name of an internal cmd.exe command!

Sending input/getting output from a console application (C#/WinForms)

I have a form with 3 controls:
A textbox for the user to enter
commands to send to a console
application,
A button to confirm the commands to
be sent and
A read-only textbox to display the
output from the application.
What I want is for the user to enter commands in the first textbox, press the button to enter and receive feedback via the second textbox.
I know how to use ProcessStartInfo.RedirectStandardOutput but, however, the app hangs when I use StandardOutput.ReadToEnd().
I had a look at the asynchronous Process.BeginOutputReadLine() but, even though my app does not hang, somehow I get no response in the textbox, it does absolutely nothing.
Here's my code:
public partial class MainForm : Form
{
private void MainForm_Load(object sender, EventArgs e)
{
InitializeInterpreter();
}
private void InitializeInterpreter()
{
InterProc.StartInfo.UseShellExecute = false;
InterProc.StartInfo.FileName = "app.exe";
InterProc.StartInfo.RedirectStandardInput = true;
InterProc.StartInfo.RedirectStandardOutput = true;
InterProc.StartInfo.RedirectStandardError = true;
InterProc.StartInfo.CreateNoWindow = true;
InterProc.OutputDataReceived += new DataReceivedEventHandler(InterProcOutputHandler);
InterProc.Start();
}
private static void InterProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
OutputTextBox.Append(Environment.NewLine + outLine.Data);
}
}
private void Enterbutton_Click(object sender, EventArgs e)
{
InterProc.StandardInput.Write(CommandtextBox.Text);
InterProc.BeginOutputReadLine();
}
}
Is there any way I can have this run smoothly? Thanks.
If you want something interactive, I got this code to work (yours modified, details on modifications below)
private void InitializeInterpreter()
{
InterProc.StartInfo.UseShellExecute = false;
InterProc.StartInfo.FileName = "Echoer.exe";
InterProc.StartInfo.RedirectStandardInput = true;
InterProc.StartInfo.RedirectStandardOutput = true;
InterProc.StartInfo.RedirectStandardError = true;
InterProc.StartInfo.CreateNoWindow = true;
InterProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
InterProc.OutputDataReceived += new DataReceivedEventHandler(InterProcOutputHandler);
bool started = InterProc.Start();
InterProc.BeginOutputReadLine();
}
private void AppendTextInBox(TextBox box, string text)
{
if (this.InvokeRequired)
{
this.Invoke((Action<TextBox, string>)AppendTextInBox, OutputTextBox, text);
}
else
{
box.Text += text;
}
}
private void InterProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
AppendTextInBox(OutputTextBox, outLine.Data + Environment.NewLine);
}
private void Enterbutton_Click(object sender, EventArgs e)
{
InterProc.StandardInput.WriteLine(CommandTextBox.Text);
}
So, I moved the BeginOutputReadLine to just after the process is started. That ensures it's really only called once. I also did an invoke required to clean up thread calls. Hopefully this should work for you.
The best solution I have found is:
private void Redirect(StreamReader input, TextBox output)
{
new Thread(a =>
{
var buffer = new char[1];
while (input.Read(buffer, 0, 1) > 0)
{
output.Dispatcher.Invoke(new Action(delegate
{
output.Text += new string(buffer);
}));
};
}).Start();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
process = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
FileName = "app.exe",
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
}
};
if (process.Start())
{
Redirect(process.StandardError, textBox1);
Redirect(process.StandardOutput, textBox1);
}
}
I've used code something like this:
public static void Run(string fileName, string arguments, out string standardOutput, out string standardError, out int exitCode)
{
Process fileProcess = new Process();
fileProcess.StartInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = arguments,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
};
bool started = fileProcess.Start();
if (started)
{
fileProcess.WaitForExit();
}
else
{
throw new Exception("Couldn't start");
}
standardOutput = fileProcess.StandardOutput.ReadToEnd();
standardError = fileProcess.StandardError.ReadToEnd();
exitCode = fileProcess.ExitCode;
}
But it's not interactive. But if the app is interactive, it'll take a lot more code anyway.
Where are you calling StandardOutput.ReadToEnd()? I once had a similar problem because I was calling Process.WaitForExit() before StandardOutput.ReadToEnd(). I had a large amount of input, and the output buffer was full before completion and my process was blocked.
You must call StandardOutput.ReadToEnd()before Process.WaitForExit().

Categories

Resources