简体   繁体   English

使用Windows USB虚拟COM端口识别断开连接事件

[英]Identify Disconnect Event with a Windows USB Virtual Com Port

I am working on an application that communicates with a USB device using a virtual serial port device driver. 我正在使用虚拟串行端口设备驱动程序与USB设备通信的应用程序。 We have run into a situation where, if the device is unplugged (or crashes) while the serial port handle is open, the only way to reconnect after the serial port handle is closed is to unplug the device and then plug it back in. 我们遇到这样的情况:如果在串口手柄打开时设备被拔出(或崩溃),在串口手柄关闭后重新连接的唯一方法是拔掉设备然后重新插入。

There are potential work-arounds if I can detect the failure quickly enough. 如果我能够足够快地检测到故障,那么有潜在的解决方法。 The problem is that, under these conditions, the following function calls do not report an error: 问题是,在这些条件下,以下函数调用不报告错误:

In my experience, the only function that returns an error when the device is unplugged is WriteFile . 根据我的经验,在拔出设备时唯一返回错误的函数是WriteFile Understandably, I don't really want to write meaningless data just to test if the port connection is still valid. 可以理解的是,我真的不想写无意义的数据只是为了测试端口连接是否仍然有效。

My question is whether there is some method that I can used to determine whether the port connection is still valid. 我的问题是,是否有一些方法可以用来确定端口连接是否仍然有效。

In case there is any question about what I am doing, the following code fragment shows what my port polling thread is doing: 如果对我正在做什么有任何疑问,以下代码片段显示我的端口轮询线程正在做什么:

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

I have encountered the same problem when using overlapped I/O as well. 我在使用重叠I / O时也遇到了同样的问题。

I am able to detect the port failure by adding the following code: 我可以通过添加以下代码来检测端口故障:

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

I am concerned about the overhead of doing this test on each poll loop. 我担心在每个轮询循环上进行此测试的开销。 I might try combining this with RegisterDeviceNotification as was suggested by MSalters. 我可能会尝试将其与RegisteralviceNotification结合使用,如MSalters所建议的那样。

Bonus Reading 奖金阅读

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM