C# add text to ListBox or TextBox - c#

Hello, I've got problem to write simple string to TextBox and ListBox. I do not know what is wrong.
After click on button3 is in class Listen running method to open communication and receiving packets. In this method (Listen.StartListen) is reference to PrintReceivedPackets. Is it mistake in Task part?
Is it better to use Thread instead of Task?
2.
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private Listen lis, start;
public Form1()
{
InitializeComponent();
}
private void button2_Click_1(object sender, EventArgs e)
{
string deviceNum = comboBox2.Text;
char dN = deviceNum[0];
start = new Listen();
Task.Factory.StartNew(() => start.StartListen(dN));
}
private void button3_Click(object sender, EventArgs e)
{
lis = new Listen();
var devices = lis.GetDevices();
comboBox2.DataSource = devices;
}
public void PrintReceivedPackets(string packetInfo)
{
// Do not work
Console.WriteLine(">>>" + packetInfo);
listBox1.Items.Add(packetInfo);
textBox1.AppendText(packetInfo);
}
}
Methods in Class Listen
public void StartListen(char deviceNum)
{
int deviceNumber = (int)Char.GetNumericValue(deviceNum);
// Take the selected adapter
PacketDevice selectedDevice = allDevices[deviceNumber - 1];
// Open the device
using (PacketCommunicator communicator =
selectedDevice.Open(65536, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
1000)) // read timeout
{
Console.WriteLine("Listening on " + selectedDevice.Description + "...");
// start the capture
communicator.ReceivePackets(0, PacketHandler);
}
}
// Callback function invoked by Pcap.Net for every incoming packet
public void PacketHandler(Packet packet)
{
Console.WriteLine(packet.Timestamp.ToString("dd-MM-yyyy ") + " length:" + packet.Length + " " + packet.DataLink);
string packetInfo = packet.Timestamp.ToString("dd-MM-yyyy ") + " length:" + packet.Length + " " + packet.DataLink;
Form1 f = new Form1();
f.PrintReceivedPackets(packetInfo);
}
Edit: added comments on how to make the main form visible to the Listener
The easiest way would be to add a reference to the main form to your Listen class. A'la:
public class Listen
{
Form1 mainForm;
public Listen(Form1 mainForm)
{
this.mainForm = mainForm;
...
}
}
Then, in button2_Click_1 you can create the start object like this:
start = new Listen(this);
And then, in PacketHandler, you can do:
mainForm.Invoke((Action)(() => mainForm.PrintReceivedPackets(packetInfo)));
And remove Form1 f = new Form1() from the PacketHandler, as you dont actually want a new form for every packet.

In the PacketHandler you are creating a new Form1 instance and then calling PrintReceivedPackets method of that form. That form, judging by the code, is never actually opened - for that you would need to call f.Show() or f.ShowSialog() at some point.
If your intent is to display the packet notification of the actual main form, then you need to do two things:
Make your main form visible to the StartListen object, by either assigning the main form object to a global variable, or pass it into StartListen as parameter.
In PacketHandler, call the PrintReceivedPackets method of the main form. It's not quite apparent, in which thread the PacketHandler is executed. If you get a "cross-thread" exception, then you need to pass the update into main thread with Invoke, something like this:
mainForm.Invoke((Action)(() => mainForm.PrintReceivedPackets(packetInfo)));

Related

Continuous serial port read with multiple events

I am new to C# and am looking for some advice on an issue I have been trying to solve in my Windows Form application.
I have an application that needs to continuously read data coming back to the program over a connected serial port. I have buttons that Open and Close the port via the user. I am having trouble configuring the "DataReceived" event handler to read the incoming data and display it in a textbox in the app.
I have been getting this error: "Cross-thread operation not valid: Control 'textBox4' accessed from a thread other than the thread it was created on." I see this is a thread error but I have not been able to figure out my issue.
namespace Program
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
getAvailabePorts();
}
private void getAvailabePorts()
{
String[] ports = SerialPort.GetPortNames();
comboBox1.Items.AddRange(ports);
}
public void button1_Click(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "" || comboBox2.Text == "")
{
textBox4.Text = "Please select port settings";
}
else
{
serialPort1.PortName = comboBox1.Text;
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
serialPort1.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
serialPort1.Open();
}
}
catch (UnauthorizedAccessException)
{
textBox4.Text = "Unauthorized Access";
}
public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
textBox4.Text = sp.ReadExisting() + "\n";
}
private void button2_Click(object sender, EventArgs e)
{
serialPort1.Close();
textBox4.Clear();
}
}
}
}
First, welcome.
Before the "big" issue (marshalling data), let me warn you -- serial ports are tricky. For example, your call to "ReadExisting" may not return what you expect -- will return whatever is in the serial port buffer at the time, but more may come in, which will overwrite what is already in your text box. So you may want to append data your text box.
Now the real issue. As a commentor mentioned, you cannot post directly post data from another thread to the UI thread. Without you knowing, the serial port created a new thread to receive data.
You can handle this directly by modifying your receiver code as follows:
public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort) sender;
var dataRcvd = sp.ReadExisting();
object[] dataArray = new object[1];
dataArray[0] = dataRcvd;
BeginInvoke( new postDataDelegate( postData), dataArray );
}
private delegate void postDataDelegate( string d );
private void postData( string d)
{
textBox4.Text = d;
}
This will "marshall" the data to the UI thread so it can be used. There are many ways this can be done (and, many differences between how it is done in WPF vs. Winforms, so watch out for that). I hope this illustrates the point.
Another aside -- no need ot make the DataReceived method public -- it will work fine private.

C# - Make UI function during like a infinite loop until button pressed

i make a code in C# where i extract some records from an Access database , but i need the while going to the next iteration to depend on the click of a button. i tried with some Thread or Tasks , but it didn't worked because it blocked the UI which i need it to be seen and clickable.
Here's the code:
bool nextClick = false ;
while (readerSelect.Read())
{
// show the correct panel
if (string.Compare(readerSelect[2].ToString(), "P1") == 0)
{
// panel with type 1
textBoxP1Text1.Text = readerSelect[3].ToString();
textBoxP1Text2.Text = readerSelect[4].ToString();
pictureBoxP1Image.ImageLocation = readerSelect[6].ToString();
}
else
{
// panel with type 2
textBoxP1Text2.Text = readerSelect[5].ToString();
}
//this while need to be kind of infinite so the interation can't be processed and
//so when i need to change iteration i click the buttonNext
while (!nextClick) {
startWhile:;
MethodInvoker mi = delegate () {
if (nextClick)
{
Application.DoEvents();
// System.Windows.Forms.Application.Run();
}
};
this.Invoke(mi);
//break;
goto startWhile;
}
private void buttonNext_Click(object sender, EventArgs e)
{
// click on the next button
nextClick = true;
}
You can use a semaphore within an async task, have the button Release it during each click, and have the while loop await it each time through. Here's a quick example, using a form that has a button1 and a label1 added to it:
public partial class Form1 : Form
{
private readonly SemaphoreSlim signal = new SemaphoreSlim(0, int.MaxValue);
public Form1()
{
this.InitializeComponent();
this.RunLoop();
}
private async void RunLoop()
{
var i = 0;
while (true)
{
this.label2.Text = $"Enqueued: {this.signal.CurrentCount}";
await this.signal.WaitAsync(); // Wait button click async
await Task.Delay(1000); // Simulate work
this.label1.Text = $"Completed: {++i}";
}
}
private void button1_Click(object sender, EventArgs e)
{
this.signal.Release();
this.label2.Text = $"Enqueued: {this.signal.CurrentCount + 1}";
// Or if you want to limit the # people can queue up, then put this whole
// thing in an `if (signal.CurrentCount < myLimit)` block, and optionally
// disable the button once limit has been reached, and re-enable it right
// before the `WaitAsync` call above.
}
}
While Dax Fohl's answer works, it seems like you've got a problem in your design. I think you're violating the Single Responsibility Principle by having too much business logic going on in the Form class.
I'd recommend factoring out the business logic into its own class. Then rather than running through everything in a loop, you simply have the button click event process the next record and display the result. Here's an example of what I mean:
public partial class Form1 : Form
{
private readonly DataProcessor dataProcessor = new DataProcessor();
public Form1()
{
this.InitializeComponent();
}
private void button1Next_Click(object sender, EventArgs e)
{
this.buttonNext.Enabled = false;
this.ProcessNext();
}
private async void ProcessNext()
{
string s = await this.dataProcessor.ProcessNext();
this.textBoxP1Text1.Text = s;
this.buttonNext.Enabled = true;
}
}
public class DataProcessor
{
private readonly Random r = new Random(); // Or reader or whatever.
public async Task<string> ProcessNext() // Just using `string` as an example.
{
await Task.Delay(1000);
return this.r.Next().ToString();
}
}
I think this will be easier to understand and more maintainable in the future. When a new team member looks at semaphore stuff (or your future self), it'll be hard to understand/remember what the point of all that was. Here, you just have a local function that does one thing and is easy to follow.

can not hit the SerialDataReceivedEvent?

All these comes from the idea that i want to use the SerialPort class in .Net , but the only way is by calling dll . Because i can only get interfaces from the program calling this dll. mycode is below.
i wrote a class about serialport,
public class CommClass
{
public SerialPort _port;
private string _receivedText;
public string receivedText
{
get { return _receivedText; }
set
{
_receivedText = value;
}
}
public CommClass(string _pname)
{
portList = SerialPort.GetPortNames();
_port = new SerialPort(portList[0]);
if (portList.Length < 1)
_port= null;
else
{
if(portList.Contains(_pname.ToUpper()))
{
_port = new SerialPort(_pname);
_port.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);
}
}
}
private void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string indata = _port.ReadExisting();
receivedText = indata;
}
}
from Bytestoread i can see there r data coming in and i can get data from port.ReadExisting(), but receivedText did not change ,it did not hit the SerialDataReceived event . Is my way wrong?any suggestion?thanks
i created a dll from CommClass ,then i call it in my winform program which has a button and a textbox . Clicking the button , then i initialize the port
public Form1()
{
InitializeComponent();
}
public CommClass mycom;
private void button1_Click(object sender, EventArgs e)
{
mycom = new CommClass("com3");
mycom._port.Open();
textbox.Text=mycom.receivedText;//i add a breakpoint at this line ,
}
when hitting it , i check mycom._port.PortName is "com3", its IsOpen() is "Open" , i use virtual port to send data . i send "1111",then check the mycom._port.BytestoRead is 4, and mycom._port.ReadExisting() is "1111", but mycom.receivedText is null. My puzzle is that i have no idea when the data is coming . How to use the DataReceived event in my winform without code "using System.Io.Ports",just with reference CommClass.dll. Did i make it clear? Thanks for help.
mycom._port.Open();
textbox.Text=mycom.receivedText;//i add a breakpoint at this line ,
That code cannot work, it is a threading race bug. The DataReceived event does not fire instantly after you open the port. It will take a microsecond or so, give or take. A threadpool thread has to get started to fire the event. And of course the device actually has to send something, they usually only do so when you transmit something first.
Which clearly did not happen, your DataReceived event handler has a bug as well. It is not allowed to update the Text property of a control in that event since it runs on a worker thread. Your program will bomb with an InvalidOperationException.
You'll have to write something like this instead:
private void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string indata = _port.ReadExisting();
this.BeginInvoke(new Action(() => {
textbox.AppendText(indata);
}));
}
With the additional stipulation that you must not leave it this way, updating the Text property of a TextBox and making it visible on the screen is an expensive operation that's going to turn your user interface catatonic when the device starts transmitting data at a high rate.

How to shown one form screen while waiting event to trigger

I need help with my program. So I have com port connection ( gps device ) and when the program starts I want to be able to show Form2 until gps eventhandler gets triggerd.
And same thing when device starts to send me data, there is some time when device is not available to give me data. How can I know in that time that I need to show again Form2?
This is a bit complicated to explain.
Here is what I have done so far. So when Form1 loads I am showing a Form2 screen which basically says that I am waiting for signal to shown up.
Now how can I put this in some sort of loop or something like that which will be always checking for eventhandler to be triggerd and in mean time I am showing this form2.
If you need more explanation let me know.
So here is the code:
private void Form1_Load(object sender, EventArgs e)
{
//open the com port when loading form
comport.Open();
Form2 my_form2 = new Form2();
my_form2.ShowDialog();
}
This is the event handler for GPS signal:
GPS.PositionReceived += new NmeaInterpreter.PositionReceivedEventHandler(GPS_PositionReceived);
And this is GPS function:
private void GPS_PositionReceived(string Lat, string Lon)
{
arrLon = Lon.Split(new char[] { '°', '"' }, StringSplitOptions.RemoveEmptyEntries);
dblLon = double.Parse(arrLon[0]) + double.Parse(arrLon[1], new System.Globalization.NumberFormatInfo()) / 60;
deciLon = arrLon[2] == "E" ? dblLon : -dblLon;
//some more code
}
So why not just close the form when the event handler triggers? that is their whole purpose.
They run code when something triggers.
Having an indefinate loop that waits for a state change is a bad idea.
private void GPS_PositionReceived(string Lat, string Lon)
{
arrLon = Lon.Split(new char[] { '°', '"' }, StringSplitOptions.RemoveEmptyEntries);
dblLon = double.Parse(arrLon[0]) + double.Parse(arrLon[1], new System.Globalization.NumberFormatInfo()) / 60;
deciLon = arrLon[2] == "E" ? dblLon : -dblLon;
//some more code
// LOAD FORM 1
// CLOSE THIS FORM (FORM 2)
}

C# Timers and repeating code

I am writing an IRC bot that needs to advertise something every few minutes to the channel. I made an attempt with the Timer but it's so messy I can't paste it here because it will be unreadable, I can post the entire file's code to pastebin though.
What needs to be looped/timed to send every 5 minutes to the channel:
public static void ChannelAdvertise(object sender, IrcEventArgs e)
{
string advertiseStream = "Live right now: ";
foreach (Monitor stream in streams)
{
if (stream.streamOnline)
{
advertiseStream += (char)3 + "03" + stream.nick +
(char)15 + " - " + stream.weblink() + " ";
}
}
irc.SendMessage(SendType.Message, e.Data.Channel,
advertiseString);
}
Just that piece of code needs to be sent to the channel every 10 minutes. Any help/pointers would be appreciated.
class Bot
{
private static System.Timers.Timer advertiseTimer;
static void Main(string[] args)
{
advertiseTimer = new System.Timers.Timer(60000);
advertiseTimer.Elapsed += new ElapsedEventHandler(advertiseTimer_Elapsed);
advertiseTimer.Start();
}
public static void ChannelAdvertise(object sender, IrcEventArgs e)
{
string advertiseStream = "Live right now :";
foreach (Monitor stream in streams)
{
if (stream.streamOnline)
{
advertiseStream += (char)3 + "03" + stream.nick + (char)15 + " - " + stream.weblink() + " ";
}
irc.SendMessage(SendType.Message, e.Data.Channel, advertiseStream);
}
}
static void advertiseTimer_Elapsed(object sender, ElapsedEventArgs e)
{
ChannelAdvertise();
}
}
You can use DispatcherTimer of the System.Windows.Threading namespace.
You can refer toMSDN Reference. Set the interval of this timer set as per your requirement and you can write this code in the timer tick event handler. Hope this helps
You should use the Dispatch Timer as #Aashish Thite has suggested.
additionally, here is your problem: you define
public static void ChannelAdvertise(object sender, IrcEventArgs e)
{
}
to recieve two arguments, but you are trying to call it with no arguments:
static void advertiseTimer_Elapsed(object sender, ElapsedEventArgs e)
{
ChannelAdvertise();
}
this is what your error message "No overload for method 'ChannelAdvertise' takes 0 arguments." is telling you.
that said, you have two options: call it with the required 2 arguments (as you have defined it), or redefine it so that it does not take any arguments (as you are calling it from the advertiseTimer_Elapsed method)
basically, you need to pass in a sender object and an IrcEventArgs argument.
so, you could call it like so:
static void advertiseTimer_Elapsed(object sender, ElapsedEventArgs e)
{
// make a new one, or get it form some collection, or whatever
IrcEventArgs args = new IrcEventArgs( /* initialize the object */ );
ChannelAdvertise(this,args);
}
or you could redefine like so:
// looking at your code, you are only using the channel from the IrcEventArgs.Data object
public static void ChannelAdvertise( channelobject channel )
{
}
Try:
void Main()
{
System.Threading.Timer timer = new System.Threading.Timer(
Callback,
channel,
0,
10 * 60 * 1000);
}
void Callback(object state)
{
ChannelAdvertise(this, (Channel)state);
}
I have recently written an article that may be just what you are looking for. It demonstrates in c# a generic polling component that runs at a specified interval and uses a background thread to perform the user action specified.
Sample usage:
IPoller poller = new UrlPoller(args[0], TimeSpan.FromSeconds(7));
IPolling pollingComponent = new Polling.Core.Polling(poller);
pollingComponent.SubscribeForPollingUpdates(PollingAction);
pollingComponent.Start();
For the code and complete sample see:
http://www.avantprime.com/blog/24/an-example-of-repeating-code-using-a-worker-thread-without-using-timers-c

Categories

Resources