I am trying to automate iperf3 using c#. The problem is that for some reason the iperf3 output is redirected once the iperf test is done and does not redirect it in real time.
public void RunIperf()
{
{
sortOutput = new StringBuilder();
this.dummyProcess .OutputDataReceived += CaptureOutput;
this.dummyProcess .ErrorDataReceived += CaptureError;
this.dummyProcess .Start();
this.dummyProcess .BeginOutputReadLine();
this.dummyProcess .BeginErrorReadLine();
this.dummyProcess .WaitForExit();
}
static void CaptureOutput(object sender, DataReceivedEventArgs e)
{
ShowOutput(e.Data, ConsoleColor.Green);
}
}
This code works for iperf2 wherein I get data on my console in real time but not for iperf3. I am not sure what the issue is.
Try using the --forceflush flag to iperf3.
Related
I have a button on a C# Windows forms application that runs the code below:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
string path = "D:\MyCsvFile.csv";
Process.Start(path);
}
Basically, the user needs to be able to make changes to a CSV file. The user is most comfortable making these changes in Microsoft Excel. I found the above code online from someone who has many years of experience working with MS Office products and C#. This guy explained that the code below works but is more error prone than using "Process.Start()" as shown above.
string path = "D:\MyCsvFile.csv";
var ExcelApp = new Excel.Application();
ExcelApp.Workbooks.OpenText(path, Comma: true);
ExcelApp.Visible = true;
This guy explains that "Process.Start()" works because Windows is set up to use Excel as the default program for opening CSV files.
Anyways, when the user clicks the button above (btnAddDataToCSV), I need my C# form to become disabled (all the buttons grayed out) until the user closes the Excel workbook that is displaying the CSV file. Potentially, I may need the C# program to read the CSV file immediately after the user closes Excel.
Ho do I do this?
I assume... I need to do something like... "disable C# form... then Wait for Excel close event... then once excel close event happens... enable C# form and read CSV file."
Below is the code I have for reading the CSV file:
string path = "D:\MyCsvFile.csv";
var reader = new StreamReader(File.OpenRead(path));
List<string> listA = new List<string>();
List<string> listB = new List<string>();
List<string> listC = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
listC.Add(values[2]);
}
I think that you need to use an event handler for process that exits.
Something like this:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
//disable form
string path = "D:\MyCsvFile.csv";
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.Start(path);
}
}
public void Excel_Exit(object sender, System.EventArgs e){
//enableForm
}
Process.Exited example in documentation: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I was unable to get the solution above to work even though the solution is similar to Microsoft's solution.
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I found this:
Why is my process's Exited method not being called?
It says:
"I've come across examples that place new Process() in a using clause. Do not do that if you want to use the Exited feature. The using clause destroys the instance along with any event handles on Exited."
It says:
using(var process = new Process())
Should be:
var process = new Process();
This makes no since because the link above that I provided for Microsoft is an example of how to use the "Process.Exited Event" and it uses "using(var process = new Process())"
But sure enough... for some reason when I tried "var process = new Process();" it worked... This is the code that worked:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
var myProcess = new Process();
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
Here is the code that does not work... But I don't understand why because it is similar to Microsoft's example.
private Process myProcess;
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
When you close excel... the event never fires... Can someone explain this? Surely Microsoft's example isn't wrong.
I want to get a console window within my form. Basically when you click button1, it runs a batch script(test.exe). I don't want a separate batch window but instead I want it to show up within my form.
I figure there are probably two ways of doing this, either 1, somehow embedding the console within my form, or 2, set StartInfo.CreateNoWindow = true; when you click button1 and get the output to funnel into a listbox to simulate a console within my form.
I am just a little stuck because I have found methods for doing both but my own testing with the various other methods people have suggested, nothing has worked. But either way, my user needs to be able to send input back to the console.
Which method would be simpler and how would I go about it?
I believe the best way to do this is to redirect output. Basically things will still execute as you want, but you will get the output wherever you want/need.
using System;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
namespace ConsoleOutput_test
{
public partial class Form1 : Form
{
Process sortProcess;
private static StringBuilder sortOutput = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
sortProcess = new Process();
sortProcess.StartInfo.FileName = "C:\\Windows\\System32\\cmd.exe";
// Set UseShellExecute to false for redirection.
sortProcess.StartInfo.CreateNoWindow = true;
sortProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
sortProcess.StartInfo.RedirectStandardOutput = true;
sortProcess.StartInfo.RedirectStandardInput = true;
sortProcess.StartInfo.RedirectStandardError = true;
sortOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
sortProcess.ErrorDataReceived += new DataReceivedEventHandler(SortErrorHandler);
// Redirect standard input as well. This stream
// is used synchronously.
sortProcess.StartInfo.RedirectStandardInput = true;
// Start the process.
sortProcess.Start();
// Start the asynchronous read of the sort output stream.
sortProcess.BeginOutputReadLine();
while (!sortProcess.HasExited)
{
Application.DoEvents(); // This keeps your form responsive by processing events
}
}
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (txtConsole.InvokeRequired) { txtConsole.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine }); }
else
{
txtConsole.AppendText(Environment.NewLine + outLine.Data);
}
}
private void SortErrorHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (txtConsole.InvokeRequired) { txtConsole.BeginInvoke(new DataReceivedEventHandler(SortErrorHandler), new[] { sendingProcess, outLine }); }
else
{
txtConsole.AppendText(Environment.NewLine + outLine.Data);
}
}
private void button2_Click(object sender, EventArgs e)
{
sortProcess.StandardInput.WriteLine(txtOutput.Text);
txtOutput.Text = "";
}
}
}
So I'm new to C#. I had been building all my tools (7 years worth of tools) in Powershell as I could never obtain a Visual Studio License but now that I have it...
I've been reading about system.componentmodel.backgroundworker however I'm having trouble implementing it.
Heres my code without Background worker and with. Can someone help me in seeing how I can implement it so my UI Doesn't go unresponsive when I kick off the Powershell jobs and I can actually get (somewhat) real updates in the text box
using System;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
namespace ServerStatusChecks
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// run our script and put the result into our textbox
// NOTE: make sure to change the path to the correct location of your script
textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
// helper method that takes your script path, loads up the script
// into a variable, and passes the variable to the RunScript method
// that will then execute the contents
private string LoadScript(string filename)
{
try
{
// Create an instance of StreamReader to read from our file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(filename))
{
// use a string builder to get all our lines from the file
StringBuilder fileContents = new StringBuilder();
// string to hold the current line
string curLine;
// loop through our file and read each line into our
// stringbuilder as we go along
while ((curLine = sr.ReadLine()) != null)
{
// read each line and MAKE SURE YOU ADD BACK THE
// LINEFEED THAT IT THE ReadLine() METHOD STRIPS OFF
fileContents.Append(curLine + "\n");
}
// call RunScript and pass in our file contents
// converted to a string
return fileContents.ToString();
}
}
catch (Exception e)
{
// Let the user know what went wrong.
string errorText = "The file could not be read:";
errorText += e.Message + "\n";
return errorText;
}
}
// Takes script text as input and runs it, then converts
// the results to a string to return to the user
private string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection<PSObject> results = pipeline.Invoke();
// close the runspace
pipeline.Dispose();
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
// return the results of the script that has
// now been converted to text
return stringBuilder.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
Now here's the code with me trying to implement BW (It's a bit of a monster as I've tried to get it to work several ways and it's a mess now)
using System;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.ComponentModel;
using System.Threading;
using System.IO;
namespace ServerStatusChecks
{
public partial class Form1 : Form
{
private BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
}
private string LoadScript(string filename)
{
try
{
// Create an instance of StreamReader to read from our file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(filename))
{
// use a string builder to get all our lines from the file
StringBuilder fileContents = new StringBuilder();
// string to hold the current line
string curLine;
// loop through our file and read each line into our
// stringbuilder as we go along
while ((curLine = sr.ReadLine()) != null)
{
// read each line and MAKE SURE YOU ADD BACK THE
// LINEFEED THAT IT THE ReadLine() METHOD STRIPS OFF
fileContents.Append(curLine + "\n");
}
// call RunScript and pass in our file contents
// converted to a string
return fileContents.ToString();
}
}
catch (Exception e)
{
// Let the user know what went wrong.
string errorText = "The file could not be read:";
errorText += e.Message + "\n";
return errorText;
}
}
private void button1_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
// run our script and put the result into our textbox
// NOTE: make sure to change the path to the correct location of your script
textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
private void bw_DoWork(object sender, EventArgs e)
{
}
// Takes script text as input and runs it, then converts
// the results to a string to return to the user
private string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection<PSObject> results = pipeline.Invoke();
// close the runspace
pipeline.Dispose();
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
// return the results of the script that has
// now been converted to text
return stringBuilder.ToString();
}
// helper method that takes your script path, loads up the script
// into a variable, and passes the variable to the RunScript method
// that will then execute the contents
private void Form1_Load(object sender, EventArgs e)
{
Thread.Sleep(1000);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
Thread.Sleep(1000);
}
}
}
The problem is that you're not running anything in the bw_DoWork event, which is where the actual background work is run. You should change this section of your code to the following:
private void button1_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, EventArgs e)
{
// run our script and put the result into our textbox
// NOTE: make sure to change the path to the correct location of your script
textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
The reason this works is that the bw_DoWork method is an event listener, which means it "listens" for when the RunWorkerAsync() method is called, which you do when the button is clicked. bw_DoWork knows to listen for the RunWorkerAsync() function is because the line you put at the top of the program:
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
This basically tells DoWork to sit and wait for the event to fire off. This is commonly referred to as "subscribing to the event". When it does, a new thread spawns that does whatever is in bw's DoWork event while your original thread, which contains the UI (and is commonly refered to as the 'UI Thread') keeps on trucking. Events and Asynchronous threads are a tricky subject to wrap your head around, but once you do it's a fantastic piece of .NET.
EDIT:
Alright, so the above is correct as far as getting bw to run properly, but it causes this error:
Error: {"Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on."}
Above I said that the main thread is called the "UI Thread". This also means that the UI is locked to that thread. Therefore, your BackgroundWorker cannot touch the UI elements. Therefore the offending line is textBox1.Text = RunScript(LoadScript(#"c:\utils\Script.ps1"));
The easiest way to fix this is to have RunScript() pass its value to a public variable, then assign the variable after bw has done its processing. We do this by subscribing to another built-in event in BackgroundWorker called RunWorkerCompleted.
First, declare a new static string at the top of the class:
static string RunScriptResult;
Then, add a new listener to bw by modifying Form1:
public Form1()
{
//...
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new bw_RunWorkerCompleted; //new listener event
}
Next, change bw_DoWork to assign the output to the string we created earlier:
private void bw_DoWork(object sender, EventArgs e)
{
RunScriptResult = RunScript(LoadScript(#"c:\utils\Script.ps1"));
}
Next, add a new event handler method for RunWorkerCompleted:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox1.Text = RunScriptResult;
}
The above event does not fire until bw is finished, and it runs in the UI thread. These modifications should get you where you need to be.
I am using the TAPI 2.0 wrapper from JulMar (https://atapi.codeplex.com/) and I'm having trouble with it.
The Initialization
void initTAPI()
{
myTAPI = new TapiManager("GetCaller");
if (!myTAPI.Initialize())
{
MessageBox.Show("FAILED!");
}else
{
name = myTAPI.Lines[0].Name;
lineName = (myTAPI != null && myTAPI.Lines.Length > 0 ? name : string.Empty);
foreach(TapiLine line in myTAPI.Lines)
{
line.NewCall += this.OnNewCall;
line.Ringing += this.OnRinging;
line.CallStateChanged += this.OnCallState;
line.CallInfoChanged += this.OnCallInfo;
}
MessageBox.Show(lineName);
}
}
So I get the lineName. When I now dial a number through the program, it fires
OnCallState
private void OnCallState(object sender, CallStateEventArgs e)
{
if (InvokeRequired == true)
{
this.BeginInvoke(new EventHandler<CallStateEventArgs>(this.OnCallState), new object[] { sender, e });
return;
}
label1.Text = "Outgoing Call...";
}
But what I actually want to do is to get the number of an incoming call, but OnCallInfo does not get fired.
OnCallInfo
private void OnCallInfo(object sender, CallInfoChangeEventArgs e)
{
if (InvokeRequired == true)
{
this.BeginInvoke(new EventHandler<CallInfoChangeEventArgs>(this.OnCallInfo), new object[] { sender, e });
return;
}
label1.Text = "Incoming Call...";
}
It says somehwere, that it only works with x86, so I changed the target but still no success.
PS: I have a call manager (ProCall) installed on the same machine, that tells me when someone calls, so I should be able to get the info in c# as well?
Here is the whole code if someone is interested: http://pastebin.com/Q5W5iGun
Depending on TSP, you may get call info messages, but TAPI does not force the driver to do this. So some TSP make you get the info yourself. In the Win32 API this is done via lineGetCallInfo.
After a quick look in this atapi wrapper, this happens in the GatherCallInfo method of the TapiCall class. However I can see no way to trigger this manually in this wrapper. You would need to modify the atapi source to make this a public method.
You can use example from TAPI which do the same. The only difference is new line.Monitor() method
foreach (TapiLine line in tapiManager.Lines)
{
try
{
line.NewCall += OnNewCall;
line.CallStateChanged += OnCallStateChanged;
line.CallInfoChanged += OnCallInfoChanged;
line.Monitor();
}
catch (TapiException ex)
{
LogError(ex.Message);
}
}
For further reading read this https://atapi.codeplex.com/SourceControl/latest#Atapi/trunk/source/test/TcMon/TapiMonitorForm.cs
I apologize if this is a duplicate question, I searched a bit and couldn't find anything similar - I have a Python library that connects to my C# application via a socket in order to allow simple Python scripting (IronPython isn't an option right now for a couple of reasons). I would like to create a Windows Forms control that would be basically a graphical front-end for the Python interpreter, so that the user could run the interpreter without having to have a separate console window open.
I attached a simple demo of what I've tried so far below, but I haven't been able to get it to work. The DataReceived event handlers are never called, and when I try to write to the standard input nothing happens in the interpreter. Does anyone have any feedback about what I'm doing wrong, or if this is even possible?
public partial class Form1 : Form
{
Process _pythonProc;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName = #"C:\Python26\Python.exe",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
_pythonProc = new Process();
_pythonProc.OutputDataReceived += OutputDataReceived;
_pythonProc.ErrorDataReceived += ErrorDataReceived;
_pythonProc.StartInfo = psi;
_pythonProc.Start();
}
private void cmdExecute_Click(object sender, EventArgs e)
{
string cmd = textInput.Text;
_pythonProc.StandardInput.WriteLine(cmd);
_pythonProc.StandardInput.Flush();
textInput.Text = string.Empty;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (!_pythonProc.HasExited)
_pythonProc.Kill();
}
private void OutputDataReceived(object sender, DataReceivedEventArgs args)
{
textOutput.Text += args.Data;
}
private void ErrorDataReceived(object sender, DataReceivedEventArgs args)
{
textOutput.Text += args.Data;
}
}
In case anyone else stumbles across this, I figured out the problem - by default, the Python interpreter only enters interactive mode if it detects that a TTY device is connected to standard input (which is normally only true if the program is run from the console). In order to redirect the standard IO streams, you have to set UseShellExecute in the ProcessStartInfo to false, which causes the interpreter to think that there is no TTY connected, meaning it immediately exits since it has nothing to do.
The solution is to run the Python interpreter with the "-i" command line argument, which forces the interpreter to interactive mode, regardless of whether there is a TTY connected to standard in. This makes the example above work correctly.
write in separate thread:
while(cond)
{
string s = _pythonProc.StandardOutput.ReadLine();
textOutput.Invoke( () => { textOutput.Text += s; } );
}