Определите событие отключения с виртуальным Com-портом Windows USB

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

Есть потенциальные обходные пути, если я смогу обнаружить ошибку достаточно быстро. Проблема заключается в том, что в этих условиях следующие вызовы функций не сообщают об ошибке: ClearCommError(), GetCommModemStatus() и ReadFile(). По моему опыту, единственная функция, которая возвращает ошибку, когда устройство отключено, - это WriteFile(). Понятно, что на самом деле я не хочу писать бессмысленные данные только для того, чтобы проверить правильность подключения к порту.

У меня вопрос, есть ли какой-то метод, который я могу использовать, чтобы определить, является ли соединение порта все еще действительным.

В случае возникновения каких-либо вопросов о том, что я делаю, следующий фрагмент кода показывает, что делает мой поток опроса портов:

        // set up the communications timeouts
        COMMTIMEOUTS timeouts;
        if(!GetCommTimeouts(port_handle, &timeouts))
           throw OsException(my_strings[strid_get_comm_timeouts_failed].c_str());
        timeouts.ReadIntervalTimeout = MAXDWORD;
        timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
        timeouts.ReadTotalTimeoutConstant = 10;
        timeouts.WriteTotalTimeoutMultiplier = 0;
        timeouts.WriteTotalTimeoutConstant = 10000;
        if(!SetCommTimeouts(port_handle, &timeouts))
           throw OsException(my_strings[strid_set_comm_timeouts_failed].c_str());
        on_open();

        // we need to set a base for the carrier detect signal.  This will be used to determine
        // when the signal "changes" while the loop executes
        bool carrier_detect_set = false;
        uint4 modem_status = 0;

        if(!GetCommModemStatus(port_handle, &modem_status))
           throw OsException(my_strings[strid_get_modem_status_failed].c_str());
        if(modem_status & MS_RLSD_ON)
           carrier_detect_set = true;

        // we are now ready to enter the main service loop for this thread.
        OVERLAPPED io_control;
        memset(&io_control, 0, sizeof(io_control));
        while(!should_close)
        {
           // we need to check to see if any comm errors have occurred
           uint4 comm_errors = 0;
           if(!ClearCommError(port_handle, &comm_errors, 0))
              throw OsException(my_strings[strid_clear_comm_errors_failed].c_str());
           if(comm_errors != 0)
              on_comm_errors(comm_errors);

           // we also need to determine if the carrier detect line has changed
           modem_status = 0;
           if(!GetCommModemStatus(port_handle, &modem_status))
              throw OsException(my_strings[strid_get_modem_status_failed].c_str());
           if(carrier_detect_set && (modem_status & MS_RLSD_ON) == 0)
              on_carrier_detect_change(false);
           else if(!carrier_detect_set && (modem_status & MS_RLSD_ON) != 0)
              on_carrier_detect_change(true);

           // we will now execute any command that might be waiting
           command_handle command;
           commands_protector.lock();
           while(!commands.empty())
           {
              command = commands.front();
              commands.pop_front();
              commands_protector.unlock();
              command->execute(this, port_handle, false);
              commands_protector.lock();
           }
           commands_protector.unlock();

           // now we will try to write anything that is pending in the write queue
           fill_write_buffer(tx_queue);
           while(!tx_queue.empty() && !should_close)
           {
              uint4 bytes_avail = tx_queue.copy(tx_buff, sizeof(tx_buff));
              uint4 bytes_written = 0;

              rcd = WriteFile(
                 port_handle, tx_buff, bytes_avail, &bytes_written, &io_control);
              if(!rcd || bytes_written == 0)
                 throw Csi::OsException(my_strings[strid_write_failed].c_str());
              if(rcd)
              {
                 SetLastError(0);
                 if(bytes_written)
                 {
                    tx_queue.pop(bytes_written);
                    on_low_level_write(tx_buff, bytes_written);
                 }
                 if(bytes_written < bytes_avail)
                    throw OsException(my_strings[strid_write_timed_out].c_str());
              }
           }

           // we will now poll to see if there is any data available to be written
           uint4 bytes_read = 0;

           rcd = ReadFile(
              port_handle, rx_buff, sizeof(rx_buff), &bytes_read, &io_control);
           if(rcd && bytes_read)
              on_low_level_read(rx_buff, bytes_read);
           else if(!rcd)
              throw OsException(my_strings[strid_read_failed].c_str());
        }

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

2 ответа

Решение

Я могу обнаружить сбой порта, добавив следующий код:

while(!should_close)
{
  // we will attempt to get and then set the comm state each time the loop repeats.
  // This will hopefully allow use to verify that the port is still valid.
  if(tx_queue.empty())
  {
      DCB dcb;
      init_dcb(&dcb);
      if(!GetCommState(port_handle, &dcb))
         throw OsException(my_strings[strid_get_modem_status_failed].c_str());
      if(!SetCommState(port_handle, &dcb))
         throw OsException(my_strings[strid_get_modem_status_failed].c_str());
 }

Я обеспокоен накладными расходами на выполнение этого теста в каждом цикле опроса. Я мог бы попытаться объединить это с RegisterDeviceNotification(), как было предложено MSalters.

Я заметил, что GetCommModemStatus() выдает 0 (все CTS, DSR, RLSD, Ring down) после отключения, но это происходит через 20 секунд.

Другие вопросы по тегам