18

我通过事件打开SerialPort并接收数据。DataReceived

有什么方法可以检测是否SerialPort断开连接?

我尝试了ErrorReceivedPinChanged事件,但没有运气。

除此之外,SerialPort.IsOpen在物理断开连接时返回 true。

4

10 回答 10

16

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 串行适配器!

于 2012-11-16T15:46:32.533 回答
3

问题是该值仅在方法执行时IsOpen被设置回。falseClose

您可以尝试捕获WM_DEVICECHANGE消息并使用它。

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

于 2012-11-16T01:56:17.463 回答
2

一旦 USB 串行适配器断开连接,我还面临无法处理的异常问题(并且无法检测/规避代码中的问题)。我可以确认 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);
        }

    }
}
于 2012-11-23T11:12:51.290 回答
1

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

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

于 2012-11-16T15:54:36.737 回答
1

为那些仍然面临这个问题的人更新;几点...

  1. 告诉用户“不要断开 USB 设备”是徒劳的,只会惹恼所有相关人员。尽管我们不希望他们这样做,但我们知道他们会在我们的程序运行时将设备拉出......
  2. 早期版本的框架确实会立即更新SerialPort.GetPortNames()yank,但 4.7 不会更新开放端口的列表。我们有一个后台线程来检查这个列表是否有变化(如上所述),它曾经工作得很好,但它不再工作了......
  3. 由于此框架更改(错误?),我们将检查线程更新为 test SerialPort.IsOpen(),在 4.7 中,当用户拉出设备时,它会立即报告设备关闭。我们确实会在 yank 上立即看到该消息,但对于我们的应用程序来说,在后台线程中WM_DEVICECHANGE进行测试更容易。SerialPort.IsOpen()
于 2020-08-15T21:20:58.933 回答
0

使用 C# SerialPort.CDHolding 标志。

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

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

于 2017-12-05T00:17:33.453 回答
0

我已经通过 Jeb 的代码编辑得到了这个解决方案。在这种情况下,我总是有一个后台计时器(但您也可以在连接 Comport 时启动它)当我连接 Comport 时,我将布尔“canDisconnect”设置为 true,这就是我使工作始终完美的方式。

    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;
            }
        
        }
    }
于 2021-12-24T09:24:00.453 回答
0

我在使用 RFID 设备时遇到了同样的问题。
可以通过发送虚拟数据来检测 USB 移除。

通常我只需要从 RFID 接收数据,但(在我的情况下)每秒发送一些数据并没有什么坏处。
当 USB 电缆断开时,serialPort.Write 会引发异常。
然后必须立即关闭端口,否则程序会挂起。

bool verify_connection()
{
    try
    {
        if (serialport.isOpen)
        {
            serialport.WriteLine("Dummy");
        }
    }
    catch (Exception ex)
    {
        serialport.Close();
    }
    return serialport.isOpen;
}

使用 .NET Core 3.1 测试

于 2021-08-15T07:03:59.870 回答
0

我用一个简单的后台工作者解决了这个问题。我不断检查/将所有当前端口设置到一个阵列中,如果新端口与我的端口阵列不同,我会从这两个阵列中获取更改后的端口。

例子:

// 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);
    }
}
于 2016-01-25T10:06:46.990 回答
0

我面临着同样的问题。经过几次尝试,我得到了我想要的东西SerialPort.DsrHolding

一旦 USB 电缆被拉出,它就会返回 false。我不知道这可能会失败的其他情况。但这对我有用。

于 2021-12-14T17:16:21.010 回答