Обнаружение, когда SerialPort отключается

Я открываю SerialPort и получаю данные через событие DataReceived.

Есть ли способ обнаружить, отключен ли SerialPort?

Я пробовал события ErrorReceived и PinChanged, но не повезло.

В дополнение к этому, SerialPort.IsOpen возвращает true при физическом отключении.

Ответ 1

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-последовательными адаптерами!

Ответ 2

Проблема заключается в том, что значение IsOpen возвращается только к false, когда выполняется метод Close.

Вы можете попробовать создать сообщение WM_DEVICECHANGE и использовать его.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx

Ответ 3

Я также столкнулся с проблемой необъяснимого исключения (и не смог обнаружить/обойти проблему в коде), как только USB-последовательный адаптер был отключен. Я могу подтвердить, что решение от 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);
        }

    }
}

Ответ 4

Если устройство, к которому вы подключаетесь, использует контакт CD, вы можете следить за тем, чтобы это изменилось (другие контакты могут применяться для некоторых устройств, использующих управление потоком). Если нет, то на самом деле нет окончательного способа сделать это.

В зависимости от ожидаемого поведения подключенного устройства вам может понадобиться реализовать тайм-аут или какое-то сохранение.

Ответ 5

Я решил эту проблему с простым фоном. Я постоянно проверяю/устанавливаю все текущие порты в массив, и если новые порты отличаются от моего порта-массива, я получаю измененные порты из этих 2 массивов.

Пример:

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

Ответ 6

Используйте флаг С# SerialPort.CDHolding.

Итак, сохраните bool до флага lastCDHolding перед вашим открытым и прочитанным циклом.

После того, как вы прочитали тест SerialPort.CDHolding!= lastCDHolding, чтобы уловить изменения или просто проверить SerialPort.CDHoldin == false, чтобы обнаружить, что порт был закрыт.