USB串行端口已拔出插头,但仍在端口列表中



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

SerialPort.GetPortNames();

不管怎样,除了当我打开一个现有的端口时,一切都很好(通过从列表中选择它)

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

然后,如果设备被拔下,它就不会从列表中删除。。我想是因为港口没有关闭。。

但是,如果我不手动关闭它,即使设备已拔出插头,我也不明白为什么它仍在列表中。。

port.Close();

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

有人能解释我的这种行为吗?

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

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

长话短说,串行端口是计算机石器时代的非常原始的设备。他们没有即插即用的支持,所以发生的事情完全不可预测。唯一明智的做法是在使用设备时永远不要拔下电缆。这并不难做到:)在这个答案中,更多关于它造成的麻烦。

这个主题可能很有趣:COM端口在拔下USB时消失。您是否尝试过处置SerialPort对象?

这可能是过时的数据效应,因为SerialPort仍在使用该com端口(未处理、注册表未更新等):

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

当你使用USB到串行适配器时,一旦拔下它,如果你试图在com端口之前将东西写入打开的端口,你就会开始收到"拒绝访问"或类似的异常。然后您可以尝试Close,然后GetPortNames应该返回正确的列表。

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是微软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());
                }
            }
        }
    }

最新更新