Stopping a continuous Event in C# with (f.ex.) a button - c#

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

Related

Sending DOS commands using Process - missing data on OutputDataReceived callback

I have been looking for a solution before posting but I gave up!
I just want to interactively send DOS command using Standard input. It works well but I always don't get the last line (the prompt line) on the OutputDataReceived callback.
Any ideas?
Process p = null;
private void Start_Click(object sender, EventArgs e)
{
p = new Process();
p.StartInfo = new ProcessStartInfo();
p.EnableRaisingEvents = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "cmd.exe";
p.ErrorDataReceived += ErrorDataReceived;
p.OutputDataReceived += OutputDataReceived;
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.StandardInput.WriteLine("dir");
}
private void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data + "\n");
}
private void WriteToStandardInput_Click(object sender, EventArgs e)
{
p.StandardInput.WriteLine(txt_command.Text); //can be "dir" or "cd temp"
}
Adding p.StandardInput.Close() solves the problem, reason is when you close the input stream it actually terminates the process (which you start using 'p.start'). So as I said, you need start separate process for each command.
~Nilesh
OK, I have found a solution... I have created 2 tasks as shown below which are reading constantly from the output and error stream and print it to a rich text box. The trick was not to use BeginErrorReadLine and BeginOutputReadLine.
I hope I was able to help others...
public partial class Form1 : Form
{
private Process p = new Process();
private SynchronizationContext context;
public Form1()
{
InitializeComponent();
context = SynchronizationContext.Current;
}
private void Start_Click (object sender, EventArgs e)
{
p.StartInfo = new ProcessStartInfo();
p.EnableRaisingEvents = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.Start();
Task.Run(() => ReadFromStreamLoop(p.StandardOutput));
Task.Run(() => ReadFromStreamLoop(p.StandardError));
}
private void ReadFromStreamLoop (StreamReader s)
{
int count = 0;
char[] buffer = new char[1024];
do
{
StringBuilder builder = new StringBuilder();
count = s.Read(buffer, 0, 1024);
builder.Append(buffer, 0, count);
context.Post(new SendOrPostCallback(delegate (object state)
{
richTextBox1.AppendText(builder.ToString());
}), null);
} while (count > 0);
}
private void WriteToStandardInput_Click (object sender, EventArgs e)
{
p.StandardInput.WriteLine(txt_command.Text); //can be "dir" or "cd temp"
}
}
If you are looking for event end of command execution, then every time spawn a new process. At the end of processing you can show the prompt on main process. Manage your environment variable through invoking(main) process.
See the example on - https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.outputdatareceived?view=netframework-4.7.2

How to identify that Process is complete now, end close this event OutputDataReceived in C#

I'm using Process of C# for open cmd and fire command. After completion of this process I want to use List which one is filled by OutputDataReceived for further use.
But, i couldn't be able to identify that Process is completes now and you can use List.
I'm getting this error. collection-was-modified-enumeration-operation-may-not-execute
Code
String exeFileName = "cmd";
List<string> list = new List<string>();
ProcessStartInfo info = new ProcessStartInfo(exeFileName);
info.Arguments = #"/c myCommand";
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
Process proc = new Process();
proc.StartInfo = info;
// Set our event handler to asynchronously read the sort output.
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
try
{
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
// i don't want to pass some time interval here like WaitForExit(1000)
// cause command takes dynamic time
proc.WaitForExit();
if (!proc.HasExited)
{
proc.Kill();
}
// this throws error like "collection-was-modified-enumeration-operation-may-not-execute"
foreach(i in list)
{
//logic
}
}
catch (Exception ex)
{
throw;
}
}
static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
// logic
list.Add(e.data);
}
}
static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
// logic
list.Add(e.data);
}

Redirecting cmd Output to textBox

I'm Re-Creating the "command prompt" into a Windows Form.
The application is not working properly; and i can't figure exactly why.
When the form Loads it is suposed to run the cmd.exe (Load the cmd info into "TextBox_Receive"), but it doesnt; and also after writing any command in the "textBox_send" (that sends input); it will only show input after pressing "Enter" key 2 or 3 times.
Any idea what i am missing here?
public partial class Form1 : Form
{
// Global Variables:
private static StringBuilder cmdOutput = null;
Process p;
StreamWriter SW;
public Form1()
{
InitializeComponent();
textBox1.Text = Directory.GetCurrentDirectory();
// TextBox1 Gets the Current Directory
}
private void Form1_Load(object sender, EventArgs e)
{
checkBox1.Checked = true;
// This checkBox activates / deactivates writing commands into the "textBox_Send"
cmdOutput = new StringBuilder("");
p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
p.Start();
SW = p.StandardInput;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
}
private static void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
// I dont actually understand this part of the code; as this is a "copy" of a snippet i found somewhere. Although it fixed one of my issues to redirect.
{
if (!String.IsNullOrEmpty(outLine.Data))
{
cmdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
// Send "Enter Key" - Send Command
if (e.KeyChar == 13)
{
SW.WriteLine(txtbox_send.Text);
txtbox_receive.Text = cmdOutput.ToString();
txtbox_send.Clear();
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
// Enable / Disable Sending Commands
if (checkBox1.Checked)
txtbox_send.Enabled = true;
else
txtbox_send.Enabled = false;
}
}
}
You might also try capturing the error data.
To do this:
after your line
p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
enter this line
p.ErrorDataReceived += new DataReceivedEventHandler(SortOutputHandler);
It might also be a problem with cmd.exe.
I think your issue is the use of OutputDataReceived. In the documentation:
The event is enabled during asynchronous read operations on
StandardOutput. To start asynchronous read operations, you must
redirect the StandardOutput stream of a Process, add your event
handler to the OutputDataReceived event, and call BeginOutputReadLine.
Thereafter, the OutputDataReceived event signals each time the process
writes a line to the redirected StandardOutput stream, until the
process exits or calls CancelOutputRead.
See the example code on that page for more details.
However - I'm not sure you need to go that route. Have you tried just reading directly from the StandardOutput stream?

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.

Print standard output of Process in event handler

I have a console application which I am running as a process from my C# program.
I have made an event handler to be called when this process terminates.
How do I print the Standard output of this process inside the event handler.
Basically, how do I access the properties of a process inside the event handler ?
My code looks like below.
public void myFunc()
{
.
.
Process p = new Process();
p.StartInfo.FileName = "myProgram.exe";
p.StartInfo.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(myProcess_Exited);
p.Start();
.
.
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine("log: {0}", <what should be here?>);
}
I do not want to make the process object p as a field of the class.
Also, what is the use of System.EventArgs e field ? How can this be used ?
In your event handler
object sender
is the Process object (that is a pretty common pattern by the way throughout the .NET Framework)
Process originalProcess = sender as Process;
Console.WriteLine("log: {0}", originalProcess.StandardOutput.ReadToEnd());
Note also that you have to set:
p.StartInfo.UseShellExecute = false;
to use IO redirection in your Process.
Use like this:
private void myProcess_Exited(object sender, System.EventArgs e)
{
Process pro = sender as Process;
string output = pro.StandardOutput.ReadToEnd()
Console.WriteLine("log: {0}", output);
}
Standart output is nothing else then StreamReader.
One option would be to capture it in a closure:
public void myFunc()
{
Process p = new Process();
p.StartInfo.FileName = "myProgram.exe";
p.StartInfo.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.Exited += new EventHandler((sender, args) => processExited(p));
p.Start();
}
private void processExited(Process p)
{
Console.WriteLine(p.ExitTime);
}

Categories