2009-11-24 160 views
0

我正在使用CreateFile打開系統上的藍牙HID設備的異步文件句柄。然後設備將開始流式傳輸數據,並使用ReadFile從設備讀取數據。問題是,如果藍牙連接斷開,ReadFile只是繼續給予ERROR_IO_PENDING而不是報告失敗。如何檢測藍牙HID設備是否斷開連接?

我不能依靠超時,因爲如果沒有任何內容需要報告,設備不會發送任何數據。如果連接仍然存在,我不希望它超時,但一段時間內根本沒有數據。

儘管如此,藍牙管理器(無論是Windows還是東芝)都會立即發現連接丟失。所以這個信息是在系統內部的某個地方;它只是沒有通過ReadFile

我提供以下信息:

  • 我不想嘗試打開它的另一個文件句柄(HANDLE值)到設備,
  • 這是用於打開手柄(但路徑時間,創建一個新的連接...)
  • OVERLAPPED結構用於異步ReadFile

我不確定這個問題是藍牙特定的,HID特定的,還是出現在一般的設備上。有什麼辦法,我可以

  • 得到ReadFile返回一個錯誤,當連接斷開,或
  • ReadFile在超時檢測迅速連接是否還活着(它需要快,因爲ReadFile被稱爲每秒至少100次),或者
  • 以另一種方式解決這個問題我還沒有想到?

回答

0

你將不得不有某種投票來檢查。除非有可以附加的事件(我不熟悉驅動程序),否則最簡單的方法是通過執行ReadFile輪詢您的COM端口,並在發送命令時檢查dwBytesRead> 0。應該有一些狀態命令可以發送,或者您可以檢查是否可以寫入端口並使用WriteFile複製寫入dwBytesWrite的那些字節,並檢查它是否等於您發送的字節長度。例如:

WriteFile(bcPort, cmd, len, &dwBytesWrite, NULL); 
if (len == dwBytesWrite) { 
    // Good! Return true 
} else 
    // Bad! Return false 
} 

這就是我在我的應用程序中如何做到的。下面可能看起來像一堆樣板代碼,但我認爲它會幫助你找到問題的根源。我首先打開Comm端口。

我有一個COM端口數組,我維護並檢查它們是否在寫入特定的COM端口之前是否打開。例如,它們在開始時打開。

int j; 
    DWORD dwBytesRead; 

    if (systemDetect == SYS_DEMO) 
     return; 

    if (port <= 0 || port >= MAX_PORT) 
     return; 

    if (hComm[port]) { 
     ShowPortMessage(true, 20, port, "Serial port already open:"); 
     return; 
    } 

    wsprintf(buff, "COM%d", port); 
    hComm[port] = CreateFile(buff, 
         GENERIC_READ | GENERIC_WRITE, 
         0, //Set of bit flags that specifies how the object can be shared 
         0, //Security Attributes 
         OPEN_EXISTING, 
         0, //Specifies the file attributes and flags for the file 
         0); //access to a template file 

    if (hComm[port] != INVALID_HANDLE_VALUE) { 
     if (GetCommState(hComm[port], &dcbCommPort)) { 
      if(baudrate == 9600) { 
        dcbCommPort.BaudRate = CBR_9600;//current baud rate 
      } else { 
       if(baudrate == 115200) { 
        dcbCommPort.BaudRate = CBR_115200; 
       } 
      } 
      dcbCommPort.fBinary = 1;  //binary mode, no EOF check 
      dcbCommPort.fParity = 0;  //enable parity checking 
      dcbCommPort.fOutxCtsFlow = 0; //CTS output flow control 
      dcbCommPort.fOutxDsrFlow = 0; //DSR output flow control 
//   dcbCommPort.fDtrControl = 1; //DTR flow control type 
      dcbCommPort.fDtrControl = 0; //DTR flow control type 
      dcbCommPort.fDsrSensitivity = 0;//DSR sensitivity 
      dcbCommPort.fTXContinueOnXoff = 0; //XOFF continues Tx 
      dcbCommPort.fOutX = 0;   //XON/XOFF out flow control 
      dcbCommPort.fInX = 0;   //XON/XOFF in flow control 
      dcbCommPort.fErrorChar = 0;  //enable error replacement 
      dcbCommPort.fNull = 0;   //enable null stripping 
      //dcbCommPort.fRtsControl = 1; //RTS flow control 
      dcbCommPort.fRtsControl = 0; //RTS flow control 
      dcbCommPort.fAbortOnError = 0; //abort reads/writes on error 
      dcbCommPort.fDummy2 = 0;  //reserved 

      dcbCommPort.XonLim = 2048;  //transmit XON threshold 
      dcbCommPort.XoffLim = 512;  //transmit XOFF threshold 
      dcbCommPort.ByteSize = 8;  //number of bits/byte, 4-8 
      dcbCommPort.Parity = 0;   //0-4=no,odd,even,mark,space 
      dcbCommPort.StopBits = 0;  //0,1,2 = 1, 1.5, 2 
      dcbCommPort.XonChar = 0x11;  //Tx and Rx XON character 
      dcbCommPort.XoffChar = 0x13; //Tx and Rx XOFF character 
      dcbCommPort.ErrorChar = 0;  //error replacement character 
      dcbCommPort.EofChar = 0;  //end of input character 
      dcbCommPort.EvtChar = 0;  //received event character 
      if (!SetCommState(hComm[port], &dcbCommPort)) { 
       setBit(SystemState, SYSTEM_PORT_ERROR); 
       //ShowPortMessage(true, 21, port, "Cannot set serial port state information:"); 
       if (!CloseHandle(hComm[port])) { 
        //ShowPortMessage(true, 22, port, "Cannot close serial port:"); 
       } 
       hComm[port] = NULL; 
       return; 
      } 
     } else { 
      setBit(SystemState, SYSTEM_PORT_ERROR); 
      //ShowPortMessage(true, 29, port, "Cannot get serial port state information:"); 
      if (!CloseHandle(hComm[port])) { 
       //ShowPortMessage(true, 22, port, "Cannot close serial port:"); 
      } 
      hComm[port] = NULL; 
      return; 
     } 

     if (!SetupComm(hComm[port], 1024*4, 1024*2)) { 
      setBit(SystemState, SYSTEM_PORT_ERROR); 
      //ShowPortMessage(true, 23, port, "Cannot set serial port I/O buffer size:"); 
      if (!CloseHandle(hComm[port])) { 
       //ShowPortMessage(true, 22, port, "Cannot close serial port:"); 
      } 
      hComm[port] = NULL; 
      return; 
     } 

     if (GetCommTimeouts(hComm[port], &ctmoOld)) { 
      memmove(&ctmoNew, &ctmoOld, sizeof(ctmoNew)); 
      //default setting 
      ctmoNew.ReadTotalTimeoutConstant = 100; 
      ctmoNew.ReadTotalTimeoutMultiplier = 0; 
      ctmoNew.WriteTotalTimeoutMultiplier = 0; 
      ctmoNew.WriteTotalTimeoutConstant = 0; 
      if (!SetCommTimeouts(hComm[port], &ctmoNew)) { 
       setBit(SystemState, SYSTEM_PORT_ERROR); 
       //ShowPortMessage(true, 24, port, "Cannot set serial port timeout information:"); 
       if (!CloseHandle(hComm[port])) { 
        //ShowPortMessage(true, 22, port, "Cannot close serial port:"); 
       } 
       hComm[port] = NULL; 
       return; 
      } 
     } else { 
      setBit(SystemState, SYSTEM_PORT_ERROR); 
      //ShowPortMessage(true, 25, port, "Cannot get serial port timeout information:"); 
      if (!CloseHandle(hComm[port])) { 
       //ShowPortMessage(true, 22, port, "Cannot close serial port:"); 
      } 
      hComm[port] = NULL; 
      return; 
     } 

     for (j = 0; j < 255; j++) { 
      if (!ReadFile(hComm[port], buff, sizeof(buff), &dwBytesRead, NULL)) { 
       setBit(SystemState, SYSTEM_PORT_ERROR); 
       //ShowPortMessage(true, 26, port, "Cannot read serial port:"); 
       j = 999; //read error 
       break; 
      } 

      if (dwBytesRead == 0) //No data in COM buffer 
       break; 

      Sleep(10); //Have to sleep certain time to let hardware flush buffer 
     } 

     if (j != 999) { 
      setBit(pcState[port], PORT_OPEN); 
     } 
    } else { 
     setBit(SystemState, SYSTEM_PORT_ERROR); 
     //ShowPortMessage(true, 28, port, "Cannot open serial port:"); 
     hComm[port] = NULL; 
    } 


HANDLE TCommPorts::OpenCommPort(void) { 

// OPEN THE COMM PORT. 
if (hComm) 
    return NULL; // if already open, don't bother 

if (systemDetect == SYS_DEMO) 
    return NULL; 

hComm = CreateFile(port, 
      GENERIC_READ | GENERIC_WRITE, 
      0, //Set of bit flags that specifies how the object can be shared 
      0, //Security Attributes 
      OPEN_EXISTING, 
      0, //Specifies the file attributes and flags for the file 
     0);//access to a template file 


// If CreateFile fails, throw an exception. CreateFile will fail if the 
// port is already open, or if the com port does not exist. 

// If the function fails, the return value is INVALID_HANDLE_VALUE. 
// To get extended error information, call GetLastError. 

if (hComm == INVALID_HANDLE_VALUE) { 
//  throw ECommError(ECommError::OPEN_ERROR); 
    return INVALID_HANDLE_VALUE; 
} 

// GET THE DCB PROPERTIES OF THE PORT WE JUST OPENED 
if (GetCommState(hComm, &dcbCommPort)) { 
    // set the properties of the port we want to use 
    dcbCommPort.BaudRate = CBR_9600;// current baud rate 
    //dcbCommPort.BaudRate = CBR_115200; 
    dcbCommPort.fBinary = 1;  // binary mode, no EOF check 
    dcbCommPort.fParity = 0;  // enable parity checking 
    dcbCommPort.fOutxCtsFlow = 0; // CTS output flow control 
    dcbCommPort.fOutxDsrFlow = 0; // DSR output flow control 
    //dcbCommPort.fDtrControl = 1; // DTR flow control type 
    dcbCommPort.fDtrControl = 0; // DTR flow control type 
    dcbCommPort.fDsrSensitivity = 0;// DSR sensitivity 
    dcbCommPort.fTXContinueOnXoff = 0; // XOFF continues Tx 
    dcbCommPort.fOutX = 0;   // XON/XOFF out flow control 
    dcbCommPort.fInX = 0;   // XON/XOFF in flow control 
    dcbCommPort.fErrorChar = 0;  // enable error replacement 
    dcbCommPort.fNull = 0;   // enable null stripping 
    //dcbCommPort.fRtsControl = 1; // RTS flow control 
    dcbCommPort.fRtsControl = 0; // RTS flow control 
    dcbCommPort.fAbortOnError = 0; // abort reads/writes on error 
    dcbCommPort.fDummy2 = 0;  // reserved 
    dcbCommPort.XonLim = 2048;  // transmit XON threshold 
    dcbCommPort.XoffLim = 512;  // transmit XOFF threshold 
    dcbCommPort.ByteSize = 8;  // number of bits/byte, 4-8 
    dcbCommPort.Parity = 0;   // 0-4=no,odd,even,mark,space 
    dcbCommPort.StopBits = 0;  // 0,1,2 = 1, 1.5, 2 
    dcbCommPort.XonChar = 0x11;  // Tx and Rx XON character 
    dcbCommPort.XoffChar = 0x13; // Tx and Rx XOFF character 
    dcbCommPort.ErrorChar = 0;  // error replacement character 
    dcbCommPort.EofChar = 0;  // end of input character 
    dcbCommPort.EvtChar = 0;  // received event character 
} 
else 
{ 
// something is way wrong, close the port and return 
    CloseHandle(hComm); 
    throw ECommError(ECommError::GETCOMMSTATE); 
} 


// SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS TO OUR SETTINGS. 
// REMEMBERTHAT THE ARGUMENT FOR BuildCommDCB MUST BE A POINTER TO A STRING. 
// ALSO NOTE THAT BuildCommDCB() DEFAULTS TO NO HANDSHAKING. 
// wsprintf(portSetting, "%s,%c,%c,%c", baud, parity, databits, stopbits); 

    dcbCommPort.DCBlength = sizeof(DCB); 
// BuildCommDCB(portSetting, &dcbCommPort); 

    if (!SetCommState(hComm, &dcbCommPort)) { 
     // something is way wrong, close the port and return 
     CloseHandle(hComm); 
     throw ECommError(ECommError::SETCOMMSTATE); 
    } 

    // set the intial size of the transmit and receive queues. 
    // I set the receive buffer to 32k, and the transmit buffer 
    // to 9k (a default). 
    if (!SetupComm(hComm, 1024*32, 1024*9)) 
    { 
     // something is hay wire, close the port and return 
     CloseHandle(hComm); 
     throw ECommError(ECommError::SETUPCOMM); 
    } 
// SET THE COMM TIMEOUTS. 
    if (GetCommTimeouts(hComm,&ctmoOld)) { 
     memmove(&ctmoNew, &ctmoOld, sizeof(ctmoNew)); 
     //default settings 
     ctmoNew.ReadTotalTimeoutConstant = 100; 
     ctmoNew.ReadTotalTimeoutMultiplier = 0; 
     ctmoNew.WriteTotalTimeoutMultiplier = 0; 
     ctmoNew.WriteTotalTimeoutConstant = 0; 
     if (!SetCommTimeouts(hComm, &ctmoNew)) { 
      // something is way wrong, close the port and return 
      CloseHandle(hComm); 
      throw ECommError(ECommError::SETCOMMTIMEOUTS); 
     } 
    } else { 
     CloseHandle(hComm); 
     throw ECommError(ECommError::GETCOMMTIMEOUTS); 
    } 

    return hComm; 
} 
+0

我沒有完全按照這種方式解決它,但你確實把我放在正確的軌道上。即使沒有任何變化,我現在也配置設備向我發送連續報告。如果我在兩秒鐘內沒有收到任何消息,我認爲它已斷開連接。 – Thomas