简体   繁体   English

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

[英]USB Serial Port Unplugged but still in the list of Ports

So I have a code which detect the devices plugged with the method所以我有一个代码来检测用该方法插入的设备

SerialPort.GetPortNames();

Anyway, everything works well except when I open an existing port (By selecting it from the list)无论如何,一切正常,除非我打开现有端口(通过从列表中选择它)

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

Then if the device is unplugged, it's not removed from the list.. I think it's because the port is not closed..然后如果设备被拔掉,它不会从列表中删除..我认为这是因为端口没有关闭..

But I can't figure out why it's still on the list if I don't close it manually, even though the device is unplugged..但是如果我不手动关闭它,我无法弄清楚为什么它仍然在列表中,即使设备已拔下。

port.Close();

Because if I open a port which is not in the list, it doesn't appear in the list then..因为如果我打开一个不在列表中的端口,它就不会出现在列表中。

Can anyone explain me this behavior ?谁能解释我这种行为?

It is entirely up to the USB device driver that emulates the serial port.这完全取决于模拟串行端口的 USB 设备驱动程序。 Unplugging a port while it is opened is in general a very bad idea.在端口打开时拔掉它通常是一个非常糟糕的主意。 There are plenty of drivers that make the port disappear, even if your SerialPort object has a handle opened on the port.有很多驱动程序会使端口消失,即使您的 SerialPort 对象在端口上打开了一个句柄。 This tends to crash a worker thread that generates the DataReceived, PinChanged and ErrorReceived events.这往往会使生成 DataReceived、PinChanged 和 ErrorReceived 事件的工作线程崩溃。 The exception is not catchable because it occurs on a worker thread, terminating your program.该异常无法捕获,因为它发生在工作线程上,从而终止了您的程序。 Some drivers even reject an attempt to close the port, making it impossible to end your program cleanly.一些驱动程序甚至拒绝关闭端口的尝试,从而无法干净地结束您的程序。

Sounds like you have a decent driver that keeps the emulated port alive as long as you don't call Close().听起来您有一个不错的驱动程序,只要您不调用 Close(),就可以使模拟端口保持活动状态。 That's a Good Thing, not a problem.这是好事,不是问题。 Don't count on this working on your user's machine, you cannot predict what kind of driver they'll get with their device.不要指望这会在您用户的机器上运行,您无法预测他们的设备会获得什么样的驱动程序。 A buying recommendation is a good idea.购买建议是个好主意。

Long story short, serial ports are very primitive devices that date from the stone age of computing.长话短说,串口是非常原始的设备,可以追溯到计算的石器时代。 There is no plug and play support for them so what happens is completely unpredictable.没有对它们的即插即用支持,所以发生的事情是完全不可预测的。 Only sane thing to do is never to unplug the cable while the device is in use.唯一明智的做法是永远不要在设备使用时拔掉电缆。 This is not hard to do :) More about the kind of trouble it causes in this answer .这并不难做到 :) 更多关于它在这个答案中引起的麻烦。

This topic might be interesting: COM port disappears when unplugging USB .这个话题可能很有趣: 拔下 USB 时 COM 端口消失 Have you tried to Dispose the SerialPort object?您是否尝试过处理 SerialPort 对象?

It could be stale data effect, because SerialPort is still using that com-port (it is not disposed, registry is not updated, etc):这可能是陈旧的数据影响,因为SerialPort 仍在使用该通信端口(它没有被处理,注册表没有更新等):

The port names are obtained from the system registry (for example, HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM).端口名称从系统注册表中获取(例如,HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM)。 If the registry contains stale or otherwise incorrect data then the GetPortNames method will return incorrect data.如果注册表包含陈旧或其他不正确的数据,则 GetPortNames 方法将返回不正确的数据。

When you are using USB-to-serial adapter, once it is unplugged you will start getting "Access denied" or something like this exception if you try to write something into opened before com-port.当您使用 USB 到串行适配器时,一旦拔下它,如果您尝试在 com-port 之前将某些内容写入打开的内容,您将开始收到“拒绝访问”或类似异常的信息。 You could try then to Close it and then GetPortNames should return proper list.您可以尝试Close它,然后GetPortNames应该返回正确的列表。

Sinatr answered correctly, it is stale data in the registry that remains stale until the opened port is closed and the resources released . Sinatr 回答正确,注册表中的陈旧数据一直保持陈旧,直到打开的端口关闭并释放资源

SerialPort.Close() does signal to release the resources, but you probably have to force a garbage collection. SerialPort.Close()确实会发出释放资源的信号,但您可能必须强制进行垃圾回收。 (I had to for my app.) (我必须为我的应用程序。)

So something like:所以像:

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

EDIT:编辑:

So, it turns out this was terribly inconsistent, even on the same machine.所以,事实证明这是非常不一致的,即使在同一台机器上也是如此。 The Nuget library SerialPortStream is an independent implementation of Microsoft's SerialPort , and gracefully catches all the bugs I had except detecting when the USB device was unplugged.该库的NuGet SerialPortStream是一个独立的执行微软的SerialPort ,优雅地捕获所有的错误我除了当USB设备是拔出检测。

My solution is now checking when the USB device is plugged back in, evident when there are duplicate entries in SerialPortStream.GetPortNames() .我的解决方案现在检查 USB 设备何时重新插入,当SerialPortStream.GetPortNames()有重复条目时很明显。 Closing the port fully closes it, so calling the garbage collector isn't necessary anymore.关闭端口会完全关闭它,因此不再需要调用垃圾收集器。

I use the following function to routinely check the connected serial ports:我使用以下函数定期检查连接的串口:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM