Reputation:
I have an open SerialPort
and receive data through the DataReceived
event.
Is there any way to detect if the SerialPort
gets disconnected?
I tried the ErrorReceived
and PinChanged
events but no luck.
In addition to that, SerialPort.IsOpen
returns true when physically disconnected.
Upvotes: 21
Views: 40506
Reputation: 11
I've got this solution with a edited of code from Jeb. I always have a background timer for such cases (but you can also start it when you connect the Comport) When I connect the Comport I set the bool "canDisconnect" to true and this is how I made the work always perfect.
bool canDisconnect = false;
private void tmrBackgroundTick_Tick(object sender, EventArgs e)
{
if (canDisconnect == true ){
//Automatic disconnect program if Comport is removed
try
{
ComPort.WriteLine("test");
}
catch (Exception ex)
{
disconnect();
canDisconnect = false;
}
}
}
Upvotes: 0
Reputation: 1
I was facing the same problem. After several tries, I got what I wanted by using SerialPort.DsrHolding
.
It retuns false as soon as the usb cable is yanked out. I don't know of other conditions where this may fail. But this worked for me.
Upvotes: 0
Reputation: 82267
I had the same problem with a RFID device.
It's possible to detect a USB removal by sending dummy data.
Normally I only have to receive data from the RFID, but it doesn't hurt (in my case) to send some data each second.
When the USB cable is disconnected the serialPort.Write throws an exception.
Then the port has to be closed immediately, else the program hangs.
bool verify_connection()
{
try
{
if (serialport.isOpen)
{
serialport.WriteLine("Dummy");
}
}
catch (Exception ex)
{
serialport.Close();
}
return serialport.isOpen;
}
Tested with .NET Core 3.1
Upvotes: 0
Reputation: 413
Update for those who are still facing this problem; a few points...
SerialPort.GetPortNames()
immediately on yank, but 4.7 does not update the list for an open port. We have a background thread that checks this list for changes (as suggested above), which used to work great, but it no longer works...SerialPort.IsOpen()
, which in 4.7 immediately reports the device closed when the user yanks out the device. We do see the WM_DEVICECHANGE
message immediately on yank but for our app its easier to just test SerialPort.IsOpen()
in the background thread.Upvotes: 3
Reputation: 322
Use the C# SerialPort.CDHolding flag.
So save the bool to a lastCDHolding flag, before your open and read loop.
After you read test SerialPort.CDHolding != lastCDHolding to trap changes or just test SerialPort.CDHoldin==false to detect the port was closed.
Upvotes: 0
Reputation: 77
I solved this problem with a simple backgroundworker. I continuously check/set all current ports into an array and if new ports are different to my ports-array I get the changed ports from these 2 arrays.
Example:
// global variable
List<string> oldPorts = new List<string>(); // contains all connected serial ports
private void bgw_checkSerialPorts_DoWork(object seder, DoWorkEventArgs e)
{
while (true)
{
string[] currentPorts = SerialPort.GetPortNames(); // get all connected serial ports
string[] diffPorts = currentPorts.Except(oldPorts.ToArray()); // get the differences between currentPorts[] and oldPorts-list
if (diffPorts.Length > 0)
{
// iterate all changed ports
for (int i = 0; i < diff.Length; i++)
{
// check if changed port was in old list
if (oldPorts.Contains(diff[i]))
{
// port in diff[] was removed
}
else
{
// port in diff[] is a new device
}
}
}
oldPorts = currentPorts.ToList(); // update oldPortlist
// check every 100ms
Thread.Sleep(100);
}
}
Upvotes: 0
Reputation: 6092
I also faced the problem of an unhandleable exception (and not being able to detect/circumvent the problem in code) as soon as the USB-Serial adapter had been disconnected. I can confirm that the solution from Mattt Burland works.
Simplified version:
class SafeSerialPort : SerialPort {
public new void Open() {
if (!base.IsOpen) {
base.Open();
GC.SuppressFinalize(this.BaseStream);
}
}
public new void Close() {
if (base.IsOpen) {
GC.ReRegisterForFinalize(this.BaseStream);
base.Close();
}
}
protected override void Dispose(bool disposing) {
try {
base.Dispose(disposing);
} catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
}
}
Upvotes: 2
Reputation:
If the device you're connecting to uses the CD pin, you can watch for that to change (other pins may apply for some devices using flow control). If not, then there isn't really a definitive way to do this.
Depending on the expected behavior of the connected device, you might want to implement a timeout or some kind of keep alive.
Upvotes: 1
Reputation: 45135
USB-serial ports are a huge pain. See, for example, this question. I'm not sure whether it really was fixed with .NET 4.0, but back in the day I tried to deal with the problem of disconnections crashing the whole program with something like this:
public class SafeSerialPort : SerialPort
{
private Stream theBaseStream;
public SafeSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
: base(portName, baudRate, parity, dataBits, stopBits)
{
}
public new void Open()
{
try
{
base.Open();
theBaseStream = BaseStream;
GC.SuppressFinalize(BaseStream);
}
catch
{
}
}
public new void Dispose()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (disposing && (base.Container != null))
{
base.Container.Dispose();
}
try
{
if (theBaseStream.CanRead)
{
theBaseStream.Close();
GC.ReRegisterForFinalize(theBaseStream);
}
}
catch
{
// ignore exception - bug with USB - serial adapters.
}
base.Dispose(disposing);
}
}
Apologies to whoever I adapted this from, it seems I failed to make a note of it in my code. The problem apparently stemmed from how .NET handled the underlying stream in the case of the serial port disappearing. It seemed you couldn't close the stream after the serial port is disconnected.
Another strategy I used was to create a small program that did just the serial communication part and exposed a WCF service for my main program to connect to. That way, when the USB-serial adapter flakes out and crashes the communication program, I can just automatically restart it from my main program.
Finally, I don't know why nobody ever marketed a locking USB port to avoid the whole accidental disconnection problem, especially with USB-serial adapters!
Upvotes: 17
Reputation: 3084
The problem is that IsOpen
value is only set back to false
when the Close
method is executed.
You could try caputuring the WM_DEVICECHANGE
message and using that.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx
Upvotes: 4