USB串行端口未插入但仍在端口列表中

时间:2014-04-23 11:53:58

标签: c# serial-port

所以我有一个代码来检测插入方法的设备

SerialPort.GetPortNames();

无论如何,一切正常,除非我打开一个现有的端口(通过从列表中选择它)

port = new SerialPort(portname, 9600);
port.Open();

然后,如果设备被拔掉,它不会从列表中删除..我认为这是因为端口没有关闭..

但我无法弄清楚为什么它仍然在列表中如果我不手动关闭它,即使设备已拔下..

port.Close();

因为如果我打开一个不在列表中的端口,它就不会出现在列表中..

有人能解释一下这种行为吗?

4 个答案:

答案 0 :(得分:6)

完全由USB设备驱动程序模拟串行端口。在端口打开时拔出端口通常是一个非常糟糕的主意。即使您的SerialPort对象在端口上打开了句柄,也有很多驱动程序使端口消失。这往往会导致生成DataReceived,PinChanged和ErrorReceived事件的工作线程崩溃。该异常不可捕获,因为它发生在工作线程上,终止您的程序。有些司机甚至拒绝尝试关闭端口,因此无法干净地结束您的程序。

听起来你有一个不错的驱动程序,只要你不调用Close()就可以保持模拟端口的存活。这是一件好事,不是问题。不要指望在用户的计算机上工作,你无法预测他们的设备会得到什么样的驱动程序。购买建议是一个好主意。

长话短说,串口是非常原始的设备,可以追溯到计算的石器时代。它们没有即插即用的支持,所以发生的事情是完全不可预测的。只有理智的事情是在设备使用时永远不要拔掉电缆。这不难做:)更多关于它在this answer中造成的麻烦。

答案 1 :(得分:1)

这个主题可能很有趣:COM port disappears when unplugging USB。您是否尝试过Dispose SerialPort对象?

答案 2 :(得分:0)

它可能是陈旧数据效果,因为SerialPortstill using该com-port(它没有处理,注册表未更新等):

  

端口名称是从系统注册表获取的(例如,HKEY_LOCAL_MACHINE \ HARDWARE \ DEVICEMAP \ SERIALCOMM)。如果注册表包含陈旧或其他不正确的数据,则GetPortNames方法将返回不正确的数据。

当您使用USB转串口适配器时,一旦拔掉它,您将开始获得“拒绝访问”或类似此异常的情况,如果您尝试在打印前打开一些内容。您可以尝试Close然后GetPortNames返回正确的列表。

答案 3 :(得分:0)

Sinatr正确回答,注册表中的陈旧数据保持陈旧,直到关闭打开的端口并释放资源为止。

SerialPort.Close()确实发出了释放资源的信号,但是您可能必须强制进行垃圾回收。 (我必须为我的应用程序。)

类似这样:

//EDIT: this actually isn't consistent, and I wouldn't recommend.
//      I recommend the notes following the EDIT below.
try
{
    if (port != null)
        port.Close(); //this will throw an exception if the port was unplugged
}
catch (Exception ex) //of type 'System.IO.IOException'
{
    System.GC.Collect();
    System.GC.WaitForPendingFinalizers();
}

port = null;

编辑:

因此,事实证明,即使在同一台机器上,这也非常不一致。 Nuget库SerialPortStream是Microsoft SerialPort的独立实现,可以优雅地捕获除检测USB设备何时拔出外的所有错误。

我的解决方案现在是检查何时重新插入USB设备,尤其是在SerialPortStream.GetPortNames()中有重复的条目时。关闭端口会完全关闭它,因此不再需要调用垃圾收集器。

我使用以下功能来例行检查连接的串行端口:

    private List<string> m_portList;
    public event EventHandler<string[]> PortListChanged;

    public void CheckForAddedDevices()
    {
        string[] portNames = SerialPortStream.GetPortNames();
        if (portNames == null || portNames.Length == 0)
        {
            if (m_portList.Count > 0)
            {
                m_portList.Clear();
                PortListChanged?.Invoke(this, null);
            }
        }
        else
        {
            if (m_portList.Count != portNames.Length)
            {
                m_portList.Clear();
                m_portList.AddRange(portNames);

                //check for duplicate serial ports (when usb is plugged in again)
                for (int i = 0; i < m_portList.Count - 1; i++)
                {
                    for (int j = i + 1; j < m_portList.Count; j++)
                    {
                        if (String.Compare(m_portList[i], m_portList[j]) == 0)
                        {
                            m_portList.Clear();
                            Close();
                        }
                    }
                }

                PortListChanged?.Invoke(this, m_portList.ToArray());
            }
            else
            {
                bool anyChange = true;
                foreach (var item in portNames)
                {
                    anyChange = true;
                    for (int i = 0; i < m_portList.Count; i++)
                    {
                        if (String.Compare(m_portList[i], item) == 0)
                        {
                            anyChange = false;
                            break;
                        }
                    }
                    if (anyChange)
                        break;
                }
                if (anyChange)
                {
                    m_portList.Clear();
                    m_portList.AddRange(portNames);

                    //check for duplicate serial ports (when usb is plugged in again)
                    for (int i = 0; i < m_portList.Count - 1; i++)
                    {
                        for (int j = i + 1; j < m_portList.Count; j++)
                        {
                            if (String.Compare(m_portList[i], m_portList[j]) == 0)
                            {
                                m_portList.Clear();
                                Close();
                            }
                        }
                    }

                    PortListChanged?.Invoke(this, m_portList.ToArray());
                }
            }
        }
    }