2013-12-20 113 views
2

我正在使用虛擬串行端口設備驅動程序與USB設備進行通信的應用程序。我們遇到了這樣的情況:如果在串行端口句柄打開的情況下拔掉設備(或崩潰),則在串行端口句柄關閉後重新連接的唯一方法是拔下設備然後重新插入。使用Windows USB虛擬COM端口識別斷開事件

如果我能夠快速檢測到故障,則可能會有解決方法。問題在於,在這些條件下,以下函數調用不報告錯誤: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()); 
     } 

我也遇到同樣的問題,當使用重疊I/O,以及。

+1

我可能會設置一個不可見的窗口並尋找[WM_DEVICECHANGE](http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v = vs.85).aspx)消息,然後從那裏處理句柄的有效性。我很確定這涉及任何類型的端口,包括USB。 – NmdMystery

+0

同意,另見'RegisterDeviceNotification'。尋找'DEV_BROADCAST_DEVICEINTERFACE'事件。 – MSalters

+0

BTW,顯然是微軟[在Windows 8中破壞了這一點](http://social.msdn.microsoft.com/Forums/windowshardware/en-US/9bf1c183-5d6e-4d43-8252-056a0d3ef958/win8-i-cant-receive -dbtdeviceremovecomplete事件換dbtdevtypport型?論壇= WDK) – MSalters

回答

1

我能夠通過添加以下代碼來檢測端口故障:

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所建議的那樣。

相關問題