Последовательный порт USB отключен, но все еще находится в списке портов
Итак, у меня есть код, который обнаруживает устройства, подключенные с помощью метода
SerialPort.GetPortNames();
В любом случае, все работает хорошо, кроме случаев, когда я открываю существующий порт (выбрав его из списка)
port = new SerialPort(portname, 9600);
port.Open();
Тогда если устройство отключено, оно не удаляется из списка.. Я думаю, что это потому, что порт не закрыт..
Но я не могу понять, почему он все еще находится в списке, если я не закрываю его вручную, даже если устройство отключено.
port.Close();
Потому что, если я открою порт, которого нет в списке, он не появится в списке...
Может кто-нибудь объяснить мне это поведение?
3 ответа
Это полностью зависит от драйвера устройства USB, который эмулирует последовательный порт. Отключение порта, когда он открыт, вообще очень плохая идея. Существует множество драйверов, которые заставляют порт исчезать, даже если у вашего объекта SerialPort есть дескриптор, открытый на порту. Это приводит к аварийному завершению рабочего потока, который генерирует события DataReceived, PinChanged и ErrorReceived. Исключение не перехватывается, потому что оно возникает в рабочем потоке, завершающем вашу программу. Некоторые драйверы даже отклоняют попытку закрыть порт, что делает невозможным чистое завершение вашей программы.
Похоже, у вас есть приличный драйвер, который поддерживает эмулируемый порт, пока вы не вызываете Close(). Это хорошо, а не проблема. Не рассчитывайте на то, что это работает на компьютере вашего пользователя, вы не можете предсказать, какой драйвер они получат на своем устройстве. Рекомендация о покупке - хорошая идея.
Короче говоря, последовательные порты являются очень примитивными устройствами, которые датируются каменным веком компьютеров. Для них нет поддержки plug and play, поэтому происходящее совершенно непредсказуемо. Единственное, что нужно делать - никогда не отсоединять кабель во время использования устройства. Это не сложно сделать:) Подробнее о том, какие проблемы это вызывает в этом ответе.
Эта тема может быть интересной: 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 - это независимая реализация MicrosoftSerialPort
, и изящно отлавливает все ошибки, которые у меня были, кроме обнаружения, когда 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());
}
}
}
}