the program freezes when i try to close close rs232 port - c#

I have written a C# Windows-forms program that reads a weight-value from an RS-232 port.
Here is my program:
try { Brate = Convert.ToInt32(MyParam._BOUD1); }
catch { Brate = 9600; }
port = new SerialPort(MyParam._COM1, Brate, Parity.None, 8, StopBits.One);
port.DtrEnable = true;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
if (port.IsOpen == false)
{
try
{
port.Open();
port.DiscardOutBuffer();
}
catch (Exception oex)
{
MessageBox.Show(oex.ToString());
}
}
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
this.Invoke(new EventHandler(DoUpdate));
}
catch (Exception ex)
{
}
}
private void DoUpdate(object s, EventArgs e)
{
try
{
lblMSG2.Text = port.ReadLine().ToString();
port.DiscardInBuffer();
}
catch (Exception ex)
{
lblMSG2.Text = ex.ToString();
}
}
This program is working excellently, except for when I try and close the form in order to open another form.
Here is how I close the serial-port:
if (port.IsOpen == true)
{
port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
try
{
port.Close();
port.DiscardOutBuffer();
}
catch (Exception oex)
{
MessageBox.Show(oex.ToString());
}
}
My program hangs, and I can't do anything except for quit the program and restart it.
What am I doing wrong?
EDIT
I tried everything suggested here
if (port.IsOpen == true)
{
//port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
try
{
//port.Dispose();
// port.DiscardOutBuffer();
// port = null;
port.Close();
}
catch (Exception oex)
{
MessageBox.Show(oex.ToString());
}
}
but still program hangs, and I can't do anything except for quit the program and restart it.
its stuck in this line: port.Close();

Your problem is that you're declaring a new event handler at the same time that you're trying to un-subscribe your existing handler.
Remove the code where it says:
port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
This is most likely what's causing your hang-up. You don't need to have that code in there at all, because once you close the port you won't receive any new data anyways. Additionally, you should simplify what you're doing when you try to close the port. Change the code where you close the port to:
try
{
if(port.IsOpen)
{
port.Close();
}
}
catch (Exception oex)
{
MessageBox.Show(oex.ToString());
}
Given that all you're doing is using a simple event handler to receive some data over the port, the above code-block is all you really need to do to close out the port.

Related

Exception after reset serial connection to a different Baud rate

In my application I'm using a 9600 baud rate serial connection and I want to use a 115200 baud rate connection for data transfer.
I've disconnected from the old connection and set it to be null value, and set my serial connection to new connection with different baud rate.
The connection is unstable and I sometimes get a System.ObjectDisposedException - what did I miss?
The connection code
public string startConnection()
{
if (serial != null)
{
serial.Dispose();
}
foreach (string portname in SerialPort.GetPortNames())
{
serial = new SerialPort(portname, 9600, Parity.None, 8, StopBits.One);
serial.ReadTimeout = 5000;
serial.WriteTimeout = 5000;
serial.Handshake = System.IO.Ports.Handshake.None;
serial.NewLine = "\n";
string received = "";
try
{
serial.Open();
serial.DiscardInBuffer();
serial.Write(":09;BATTERY;");
Thread.Sleep(500);
received = serial.ReadLine();
if (received.Contains(";BATTERY;V="))
{
status = SERIAL_CONNECTED;
return portname;
}
}
catch (Exception err)
{
try
{
serial.Close();
status = DISCONNECTED;
}
catch (Exception)
{
// throw;
}
}
}
throw new Exception("couldn't connect to coms");
//return "couldn't connect to coms";
//this.Close();
}
Disconnect function:
public void disconnect ()
{
if (serial == null || serial.IsOpen==false ||status == DISCONNECTED)
return;
status = DISCONNECTED;
serial.Close();
serial = null;
}
The main program is:
private async void BurnOFP_click(object sender, RoutedEventArgs e)
{
startConnection();
some actions.............
disconnect();
var t = new Task(() =>
{
try
{
myUswm.startModemConnection(); // same but with different baud rate
}
catch (Exception e2) { MessageBox.Show(e2.Message); }
});
t.Start();
t.Wait();
modem = new XMODEM_FullDotNET(myUswm.getSerialPort(), XMODEM_FullDotNET.Variants.XModemCRC);
buff = File.ReadAllBytes(softwareFilePath_Text.Text);
if (buff.Length < 1)
{
MessageBox.Show("ERROR : wrong OFP file");
return;
}
if (myUswm.prepareOFPBurning()) // sends u to start transfer
{
if (isBurning == false)
{
isBurning = true;
modem._ProgressSent = 0;
myProgBar = new myProgressBar(modem);
myProgBar.StartTransfer(modem, buff.Length);
myProgBar.Show(); // show window
// got the Exception here!!!!!!!!!!
var t3 = new Task(() =>
{
modem.Send(buff);
});
............
}
else
MessageBox.Show("burning in progress..");
}
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
Thanks for any help
RESOLVED
my problem was A bad timing caused by closing and reopen the same port.
I've found the information in MSDN Serial class:
The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly.
my solution was keeping the connection alive and change the baud rate and update the connection status in my application manually.

Issue with opening and closing serial port

I am currently working on C# application which requires to read serial port. In UI, there is a ON/OFF button which enables user click on it to start and stop reading data from serial port. If I continuously click on the button on and off. It threw an exception - Access to COM3 is denied or even said "The device is not connected". Can anyone suggest a better way to implement the serial port function which is able to resolve the situation as described above? Here is the code I use:
**// Start reading data from serial port**
public override void StartReading(string portname)
{
try
{
int k = int.Parse(portname.Replace("COM", ""));
if (startThread != null)
{
startThread.Abort();
startThread = null;
}
startThread = new Thread(new ThreadStart(delegate
{
isActive = true;
try
{
using (SerialPort sp = new SerialPort(portname))
{
if (!isActive)
{
DisposeBT(sp);
return;
}
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
if (!isActive)
{
DisposeBT(sp);
return;
}
if (!isActive)
{
DisposeBT(sp);
return;
}
else
{
Thread.Sleep(6500);
try
{
if (sp != null && !sp.IsOpen)
{
sp.Open();
}
}
catch (Exception ex)
{
Logger.Warn("Failed to open the serial port for HRM once. Try it again.");
Logger.Error(ex);
////////////////////// new added below
if(sp !=null && sp.IsOpen)
{
sp.Dispose();
}
Thread.Sleep(6500);
if (IsPortAvailable(k))
{
try
{
if (sp != null && !sp.IsOpen)
{
sp.Open();
}
}
catch (Exception ex1)
{
////////////////////// new added below
if (sp != null && sp.IsOpen)
{
sp.Dispose();
}
Logger.Warn("Failed to open the serial for HRM twice.");
Logger.Error(ex1);
// return;
}
}
}
}
while (true)
{
if (!isActive)
{
DisposeBT(sp);
break;
}
}
if (!isActive)
{
DisposeBT(sp);
return;
}
DisposeBT(sp);
}
}
catch (Exception ex)
{
Logger.Warn("Exception thrown for HRM.");
Logger.Error(ex);
}
}));
startThread.IsBackground = true;
startThread.Priority = ThreadPriority.Highest;
startThread.Start();
}
catch (Exception ex)
{
Logger.Warn("Failed to start reading for HRM02I3A1 bluetooth device.");
Logger.Error(ex);
}
}
// Stop reading data from serial port
public override void StopReading()
{
try
{
isActive = false;
}
catch { }
}
// event handler for the serial port to read data from sp.
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (isActive)// && startThread.IsAlive
{
SerialPort sp1 = (SerialPort)sender;
try
{
sp1.Read(data, 0, 8);
decoder.Decode(data);
}
catch(Exception ex)
{
Logger.Warn("------data received from Serial Port error for HRM-------");
Logger.Error(ex);
};
}
}
first make background worker thread that accept the cancel event.
in the DoWork method you can write something like that
void DoWork{
// init com port
while(no body cancelled the background worker){
// if there any waiting data receive and process it. do not use event handlers
}
// close the serial port so you can open it again later.
}
Also if you want to cancel the background work it would be a piece of cake
// send cancel command.
// wait till it is canceled.
Try adding startThread.Join() directly after the call to startThread.Abort().
Take a look at the msdn documentation on Thread.Abort and perhaps you also should check what join does.

C# Serial Port communication issue

I have a problem with a small C# application.
The application has to connect through a serial port to a bar code scanner which reads a Data Matrix code. The Data Matrix code represents an array of bytes which is a zip archive. I read a lot about the way SerialPort.DataReceived work but I can't find an elegant solution to my problem. And the application should work with different bar code scanners so i can't make it scanner specific. Here is some of my code:
using System;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;
using Ionic.Zip;
namespace SIUI_PE
{
public partial class Form1 : Form
{
SerialPort _serialPort;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
_serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.ToString());
return;
}
_serialPort.Handshake = Handshake.None;
_serialPort.ReadBufferSize = 10000;
_serialPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
_serialPort.Open();
}
void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] data = new byte[10000];
_serialPort.Read(data, 0, 10000);
File.WriteAllBytes(Directory.GetCurrentDirectory() + "/temp/fis.zip", data);
try
{
using (ZipFile zip = ZipFile.Read(Directory.GetCurrentDirectory() + "/temp/fis.zip"))
{
foreach (ZipEntry ZE in zip)
{
ZE.Extract(Directory.GetCurrentDirectory() + "/temp");
}
}
File.Delete(Directory.GetCurrentDirectory() + "/temp/fis.zip");
}
catch (Exception ex1)
{
MessageBox.Show("Corrupt Archive: " + ex1.ToString());
}
}
}
}
So my question is: How can I know that I read all the bytes the scanner sent?
The code I've got for reading barcode data, which has been working flawlessly in production for several years looks like this:
Note, my app has to read standard UPC barcodes as well as GS1 DataBar, so there's a bit of code you may not need...
The key line in this is:
string ScanData = ScannerPort.ReadExisting();
which is found in the DoScan section, and simply reads the scan data as a string. It bypasses the need to know how many bytes are sent, and makes the rest of the code easier to deal with.
// This snippet is in the Form_Load event, and it initializes teh scanner
InitializeScanner();
ScannerPort.ReadExisting();
System.Threading.Thread.Sleep(1000);
// ens snippet from Form_Load.
this.ScannerPort.DataReceived += new SerialDataReceivedEventHandler(ScannerPort_DataReceived);
delegate void DoScanCallback(); // used for updating the form UI
void DoScan()
{
if (this.txtCouponCount.InvokeRequired)
{
DoScanCallback d = new DoScanCallback(DoScan);
this.Invoke(d);
return;
}
System.Threading.Thread.Sleep(100);
string ScanData = ScannerPort.ReadExisting();
if (isInScanMode)
{
try
{
HandleScanData(ScanData);
}
catch (Exception ex)
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show("Invalid Scan");
}
}
}
void ScannerPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
// this call to sleep allows the scanner to receive the entire scan.
// without this sleep, we've found that we get only a partial scan.
try
{
DoScan();
}
catch (Exception ex)
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show("Unable to handle scan event in ScannerPort_DataReceived." + System.Environment.NewLine + ex.ToString());
}
}
void Port_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show(e.EventType.ToString());
}
private void HandleScanData(string ScanData)
{
//MessageBox.Show(ScanData + System.Environment.NewLine + ScanData.Length.ToString());
//Determine which type of barcode has been scanned, and handle appropriately.
if (ScanData.StartsWith("A") && ScanData.Length == 14)
{
try
{
ProcessUpcCoupon(ScanData);
}
catch (Exception ex)
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show("Unable to process UPC coupon data" + System.Environment.NewLine + ex.ToString());
}
}
else if (ScanData.StartsWith("8110"))
{
try
{
ProcessDataBarCoupon(ScanData);
}
catch (Exception ex)
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show("Unable to process DataBar coupon data" + System.Environment.NewLine + ex.ToString());
}
}
else
{
System.Media.SystemSounds.Beep.Play();
MessageBox.Show("Invalid Scan" + System.Environment.NewLine + ScanData);
}
}
private void InitializeScanner()
{
try
{
ScannerPort.PortName = Properties.Settings.Default.ScannerPort;
ScannerPort.ReadBufferSize = Properties.Settings.Default.ScannerReadBufferSize;
ScannerPort.Open();
ScannerPort.BaudRate = Properties.Settings.Default.ScannerBaudRate;
ScannerPort.DataBits = Properties.Settings.Default.ScannerDataBit;
ScannerPort.StopBits = Properties.Settings.Default.ScannerStopBit;
ScannerPort.Parity = Properties.Settings.Default.ScannerParity;
ScannerPort.ReadTimeout = Properties.Settings.Default.ScannerReadTimeout;
ScannerPort.DtrEnable = Properties.Settings.Default.ScannerDtrEnable;
ScannerPort.RtsEnable = Properties.Settings.Default.ScannerRtsEnable;
}
catch (Exception ex)
{
MessageBox.Show("Unable to initialize scanner. The error message received will be shown next. You should close this program and try again. If the problem persists, please contact support.", "Error initializing scanner");
MessageBox.Show(ex.Message);
Application.Exit();
}
}
As stated in the doc for SerialPort.DataReceived, "Use the BytesToRead property to determine how much data is left to be read in the buffer."
here is the doc for SerialPort.BytesToRead
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.bytestoread.aspx

How do I properly close serial port connection and thread it runs in?

I have an serial port read running on a background thread, but everytime I try to close it, I get an exception. Usually an IO-exception.
It is as if the read continues on eventhough I close the thread.
This is my current code:
EDIT: I changed the code, removed the checks on threatstate.
public bool Connect(string portName)
{
try
{
sp = new SerialPort(portName, BaudRate);
sp.Open();
sp.DtrEnable = true;
cf = SingletonFormProvider.GetInstance<ConnectionForm>(null);
_continue = true;
readThread = new Thread(Read);
readThread.Start();
return true;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
public void Disconnect()
{
if (IsConnectionOpen)
{
_continue = false;
readThread.Abort();
while (readThread.ThreadState == ThreadState.AbortRequested)
{ }
sp.Close();
readThread.Join();
}
}
private void Read()
{
while (_continue)
{
try
{
string message = sp.ReadLine();
cf.WriteLog(message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
_continue = false;
readThread.Join();
sp.Close();
}
}
}
You do know that what thread.Abort() does is throw a ThreadAbortException in the thread in question, right? In the thread you catch all exceptions that inherited from Exception. I believe that includes the abort exception. If you really must call Abort, you may want to close the serial port first since I believe that will cause any pending calls to return.
Join your read thread before you close the serial port:
public void Disconnect()
{
if (IsConnectionOpen)
{
_continue = false;
readThread.Join();
sp.Close();
}
}

Instead of scanning multiple ports I'm scanning only one and when port is closed my app closes

I'm trying to scan multiple ports at once using asynchronymous scanning. The problem is that I can only display the first working port and then waiting like 20 seconds my app is closing with out telling me that the port is closed.
What could be wrong with this code?
private void btnStart_Click(object sender, EventArgs e)
{
for (int port = 80; port < 100; port++)
{
ScanPort(port);
}
}
private void ScanPort(int port)
{
var client = new TcpClient();
try
{
client.BeginConnect(IPAddress.Parse("74.125.226.84"), port, new AsyncCallback(CallBack), client);
}
catch (SocketException)
{
client.Close();
}
}
private void CallBack(IAsyncResult result)
{
var client = (TcpClient)result.AsyncState;
client.EndConnect(result);
if (client.Connected)
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "open2" + Environment.NewLine;
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "closed2" + Environment.NewLine;
});
}
}
In your callback method, I would make sure close the connection and dispose of the TcpClient. Also TcpClient.EndConnect(IAsyncResult) can also throw exceptions. I also do not see where capturing the port number for display to the user. I would write the callback something like this.
Edit: I didn't actually compile or execute my code (sorry). I also found this other article that shows how to create a port scanner in C#, http://www.dijksterhuis.org/building-a-simple-portscanner-in-c/ There is a comment in this post stating,
There is a gotcha here : The .NET implementation of TCPClient.Close() function does not actually close the connection properly. So we need to do the additional steps of obtaining the stream representing the connection and closing this as well before calling TCPClient.Close.
private void CallBack(IAsyncResult result)
{
var client = (TcpClient)result.AsyncState;
bool connected = false;
try
{
client.EndConnect(result);
connected = client.Connected;
}
catch (SocketException)
{
}
catch (ObjectDisposedException)
{
}
finally
{
if (client.Connected)
{
client.Close();
}
client.Dispose();
}
if (connected)
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "open2" + Environment.NewLine;
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "closed2" + Environment.NewLine;
});
}
}

Categories