Обнаружение, когда SerialPort отключается
У меня открытая SerialPort
и получать данные через DataReceived
событие.
Есть ли способ обнаружить, если SerialPort
отключается?
Я попробовал ErrorReceived
а также PinChanged
события, но не повезло.
В дополнение к этому, SerialPort.IsOpen
возвращает true, когда физически отключен.
10 ответов
USB-последовательные порты - огромная боль. Смотрите, например, этот вопрос. Я не уверен, действительно ли это было исправлено в.NET 4.0, но в тот день я пытался решить проблему с отключением, приводящим к сбою всей программы, примерно так:
public class SafeSerialPort : SerialPort
{
private Stream theBaseStream;
public SafeSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
: base(portName, baudRate, parity, dataBits, stopBits)
{
}
public new void Open()
{
try
{
base.Open();
theBaseStream = BaseStream;
GC.SuppressFinalize(BaseStream);
}
catch
{
}
}
public new void Dispose()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (disposing && (base.Container != null))
{
base.Container.Dispose();
}
try
{
if (theBaseStream.CanRead)
{
theBaseStream.Close();
GC.ReRegisterForFinalize(theBaseStream);
}
}
catch
{
// ignore exception - bug with USB - serial adapters.
}
base.Dispose(disposing);
}
}
Извинения тому, от кого я это адаптировал, кажется, что я не смог сделать это в своем коде. Проблема, по-видимому, связана с тем, как.NET обрабатывает основной поток в случае исчезновения последовательного порта. Казалось, вы не можете закрыть поток после отключения последовательного порта.
Другая стратегия, которую я использовал, состояла в том, чтобы создать небольшую программу, которая выполняла бы только часть последовательной связи и предоставляла сервис WCF для моей основной программы для подключения. Таким образом, когда USB-адаптер отключается и выходит из строя коммуникационная программа, я могу просто автоматически перезапустить его из моей основной программы.
Наконец, я не знаю, почему никто никогда не продавал блокирующий USB-порт, чтобы избежать проблемы случайного отключения, особенно с USB-последовательными адаптерами!
Проблема в том, что IsOpen
значение только возвращается false
когда Close
метод выполнен.
Вы могли бы попытаться захватить WM_DEVICECHANGE
сообщение и используя это.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx
Я также столкнулся с проблемой необратимого исключения (и не смог обнаружить / обойти проблему в коде), как только USB-Serial адаптер был отключен. Я могу подтвердить, что решение от Mattt Burland работает.
Упрощенная версия:
class SafeSerialPort : SerialPort {
public new void Open() {
if (!base.IsOpen) {
base.Open();
GC.SuppressFinalize(this.BaseStream);
}
}
public new void Close() {
if (base.IsOpen) {
GC.ReRegisterForFinalize(this.BaseStream);
base.Close();
}
}
protected override void Dispose(bool disposing) {
try {
base.Dispose(disposing);
} catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
}
}
Обновление для тех, кто все еще сталкивается с этой проблемой; несколько моментов...
- Говорить пользователям "не отключайте USB-устройство" бесполезно и просто раздражает всех участников. Даже если бы мы этого не хотели, мы знаем, что они выдергивают устройство во время работы нашей программы...
- Более ранние версии базы сделали обновить
SerialPort.GetPortNames()
сразу на рывке, но 4.7 не обновляет список для открытого порта. У нас есть фоновый поток, который проверяет этот список на наличие изменений (как предлагалось выше), который раньше работал отлично, но больше не работает... - Из-за этого изменения структуры (ошибка?) Мы обновили поток проверки, чтобы проверить
SerialPort.IsOpen()
, который в версии 4.7 немедленно сообщает о закрытии устройства, когда пользователь вытаскивает устройство. Мы действительно видимWM_DEVICECHANGE
сообщение сразу при копировании, но для нашего приложения проще просто протестироватьSerialPort.IsOpen()
в фоновом потоке.
Надеюсь, что это поможет, С уважением, Дэйв
Если устройство, к которому вы подключаетесь, использует контакт CD, вы можете следить за его изменением (другие контакты могут применяться для некоторых устройств, использующих управление потоком). Если нет, то на самом деле не существует определенного способа сделать это.
В зависимости от ожидаемого поведения подключенного устройства может потребоваться установить тайм-аут или какой-либо вид поддержки активности.
Я решил эту проблему с простым фоновым работником. Я постоянно проверяю / устанавливаю все текущие порты в массив, и если новые порты отличаются от моего массива портов, я получаю измененные порты из этих двух массивов.
Пример:
// global variable
List<string> oldPorts = new List<string>(); // contains all connected serial ports
private void bgw_checkSerialPorts_DoWork(object seder, DoWorkEventArgs e)
{
while (true)
{
string[] currentPorts = SerialPort.GetPortNames(); // get all connected serial ports
string[] diffPorts = currentPorts.Except(oldPorts.ToArray()); // get the differences between currentPorts[] and oldPorts-list
if (diffPorts.Length > 0)
{
// iterate all changed ports
for (int i = 0; i < diff.Length; i++)
{
// check if changed port was in old list
if (oldPorts.Contains(diff[i]))
{
// port in diff[] was removed
}
else
{
// port in diff[] is a new device
}
}
}
oldPorts = currentPorts.ToList(); // update oldPortlist
// check every 100ms
Thread.Sleep(100);
}
}
Используйте флаг C# SerialPort.CDHolding.
Поэтому сохраните bool во флаг lastCDHolding перед циклом открытия и чтения.
После того, как вы прочитали тест SerialPort.CDHolding!= LastCDHolding, чтобы перехватить изменения, или просто проверить SerialPort.CDHoldin==false, чтобы определить, что порт был закрыт.
У меня была такая же проблема с устройством RFID.
Можно обнаружить отключение USB, отправив фиктивные данные.
Обычно мне нужно получать данные только от RFID, но не повредит (в моем случае) посылать какие-то данные каждую секунду.
Когда USB-кабель отключен, serialPort.Write выдает исключение.
Затем необходимо немедленно закрыть порт, иначе программа зависнет.
bool verify_connection()
{
try
{
if (serialport.isOpen)
{
serialport.WriteLine("Dummy");
}
}
catch (Exception ex)
{
serialport.Close();
}
return serialport.isOpen;
}
Протестировано с .NET Core 3.1
Я столкнулся с той же проблемой. После нескольких попыток я получил то, что хотел, используя
Он возвращает false, как только кабель USB выдергивается. Я не знаю других условий, при которых это может потерпеть неудачу. Но это сработало для меня.
У меня есть это решение с отредактированным кодом от Джеба. У меня всегда есть фоновый таймер для таких случаев (но вы также можете запустить его при подключении Comport). Когда я подключаю Comport, я устанавливаю логическое значение «canDisconnect» в true, и таким образом я делаю работу всегда идеальной.
bool canDisconnect = false;
private void tmrBackgroundTick_Tick(object sender, EventArgs e)
{
if (canDisconnect == true ){
//Automatic disconnect program if Comport is removed
try
{
ComPort.WriteLine("test");
}
catch (Exception ex)
{
disconnect();
canDisconnect = false;
}
}
}