问题 检测SerialPort何时断开连接


我有一个开放的 SerialPort 并通过。接收数据 DataReceived 事件。

有没有办法检测是否 SerialPort 断开连接?

我试过了 ErrorReceived 和 PinChanged 事件,但没有运气。

在此之上, SerialPort.IsOpen 物理断开时返回true。


5938
2017-11-16 00:12


起源

这是USB串口适配器吗? - Matt Burland
@Matt是的,它是一个USB串口适配器。谢谢。
麻烦不值得,大多数USB驱动程序都要处理这个问题 非常 不好。他们只是让整个设备消失,即使你打开了一个手柄。这往往会很糟糕,工作线程中的一个无法捕获的异常很常见。或者最糟糕的一种,驱动程序甚至不让你关闭端口,需要重启。在Windows写入时,将闪存驱动器拉出来是同样的智慧:不要这样做。使用“安全删除硬件”,该对话框应提示用户关闭您的程序。 - Hans Passant


答案:


USB串口是一个 巨大 疼痛。例如,参见 这个问题。我不确定它是否真的是用.NET 4.0修复的,但是当天我试图处理断开连接的问题,整个程序崩溃了,如下所示:

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

向我改编的人道歉,似乎我没有在我的代码中记下它。问题显然源于.NET在串口消失的情况下如何处理底层流。串口断开后,似乎无法关闭流。

我使用的另一个策略是创建一个只执行串行通信部分的小程序,并为我的主程序提供WCF服务以进行连接。这样,当USB串行适配器脱落并崩溃通信程序时,我可以从主程序中自动重启它。

最后,我不知道为什么没有人销售锁定USB端口以避免整个意外断开问题,尤其是USB串口适配器!


10
2017-11-16 15:46





问题是 IsOpen 值只能回到 false 当。。。的时候 Close 方法被执行。

你可以尝试捕捉 WM_DEVICECHANGE 消息并使用它。

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx


2
2017-11-16 01:56



这不是只是告诉你是否有人卸载了串口本身? - Jon B
@JonB在Usb适配器的情况下我的猜测是,如果你拔掉Usb设备会发生什么! - Peter


一旦USB-Serial适配器断开连接,我也遇到了一个不可处理的异常问题(并且无法检测/规避代码中的问题)。我可以确认Mattt Burland的解决方案是有效的。

简化版:

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

    }
}

2
2017-11-23 11:12



它对我不起作用 - 这种dispose方法抛出异常“未处理的处置...” - Uri Abramson
什么是抛出的确切异常/内部异常?这条线 base.Dispose(disposing); 由于SerialToUSB适配器,预计会抛出异常,但它应该由try / catch处理。 - Rev1.0
例外情况说“安全处理已关闭”。这可能是因为SerialStream.EventLoopRunner.WaitForCommResult(..)正在运行一个试图访问句柄的内部循环。你是怎么解决这个问题的? - Uri Abramson
它是微软众所周知的问题。这是一个解决方法: social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/... - Uri Abramson


如果您要连接的设备使用CD引脚,您可以注意要更改(其他引脚可能适用于使用流量控制的某些设备)。如果没有,那么就没有明确的方法来做到这一点。

根据所连接设备的预期行为,您可能希望实现超时或某种保持活动状态。


1
2017-11-16 15:54





我用一个简单的背景工作者解决了这个问题。 我不断检查/设置所有当前端口到一个阵列,如果新端口与我的ports-array不同,我从这两个阵列获得更改的端口。

例:

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

1
2018-01-25 10:06



除非,如果我打开串口,它将在删除后从GetPortNames列表中消失。如果端口尚未打开,这确实可以正常工作。 - rocketsarefast
此代码无法解决问题!代码是无稽之谈 - Elmue


使用C#SerialPort.CDHolding标志。

因此,在打开和读取循环之前,将bool保存到lastCDHolding标志。

在您阅读测试SerialPort.CDHolding!= lastCDHolding以捕获更改或仅测试SerialPort.CDHoldin == false以检测端口已关闭之后。


0
2017-12-05 00:17