【问题标题】:Detecting when a SerialPort gets disconnected检测 SerialPort 何时断开连接
【发布时间】:2012-11-16 00:12:06
【问题描述】:

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

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

我尝试了ErrorReceivedPinChanged 事件,但没有成功。

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

【问题讨论】:

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

标签: .net winforms


【解决方案1】:

USB 串行端口是一个巨大的痛苦。例如,参见this question。我不确定它是否真的被 .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 串行适配器!

【讨论】:

    【解决方案2】:

    问题是IsOpen的值只有在执行Close方法时才被设置回false

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

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

    【讨论】:

    • 这不会告诉你是否有人卸载了串口本身吗?
    • @JonB 如果是 USB 适配器,我猜如果你拔掉 USB 设备会发生这种情况!
    【解决方案3】:

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

    【讨论】:

    • 它对我不起作用 - 这个 dispose 方法会抛出异常“unhandled dispose...”
    • 抛出的确切异常/内部异常是什么?由于 SerialToUSB 适配器,base.Dispose(disposing); 行预计会引发异常,但应由 try/catch 处理。
    • 异常显示“安全句柄已关闭”。这可能是因为 SerialStream.EventLoopRunner.WaitForCommResult(..) 正在运行试图访问句柄的内部循环。您是如何解决此问题的?
    • 这是 Microsoft 已知问题。这是一个解决方法:social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/…
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

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

        例子:

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

        【讨论】:

        • 除了,如果我打开了串口,删除后它不会从 GetPortNames 列表中消失。如果端口尚未打开,这确实可以正常工作。
        • 这段代码不能解决问题!代码是废话
        【解决方案7】:

        使用 C# SerialPort.CDHolding 标志。

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

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

        【讨论】:

          【解决方案8】:

          我在使用 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 测试

          【讨论】:

            【解决方案9】:

            我也遇到了同样的问题。经过几次尝试,我通过SerialPort.DsrHolding得到了我想要的。

            USB 线拔出后立即返回 false。我不知道这可能会失败的其他情况。但这对我有用。

            【讨论】:

              【解决方案10】:

              我通过 Jeb 编辑的代码得到了这个解决方案。 对于这种情况,我总是有一个后台计时器(但你也可以在连接 Comport 时启动它)当我连接 Comport 时,我将 bool "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;
                          }
                      
                      }
                  }
              

              【讨论】:

                猜你喜欢
                • 2017-02-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-11-03
                • 1970-01-01
                • 1970-01-01
                • 2012-12-10
                • 2013-07-22
                相关资源
                最近更新 更多