所以我有一个代码,可以检测用方法插入的设备
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());
}
}
}
}