1

Meine Anwendung verwendet einen separaten Thread für die asynchrone Verarbeitung empfangener serieller Daten. Der PC gelangt wie erwartet in den Empfangs-Handler, aber von dort gehen die Dinge merkwürdig aus.Serieller Port empfängt Thread, der sich nicht erwartungsgemäß verhält - C++

Das ist meine Thread-Funktion:

// Create event for OVERLAPPED structure. 
s_ov.hEvent = ::CreateEvent(
    NULL,       // No security 
    TRUE,       // Create a manual-reset event object 
    FALSE,       // Initial state is non-signaled 
    NULL       // No name specified 
    ); 

// Load event handles. 
pHandles[0] = s_hSerialPortRxThreadExitEvent; 

while (bContinue) 
{ 
    if (!::WaitCommEvent(s_hSerialPort, &dwEventMask, &s_ov)) 
    { 
     if (::GetLastError() != ERROR_IO_PENDING) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 
    } 

    pHandles[1] = s_ov.hEvent; 

    dwObjectWaitState = ::WaitForMultipleObjects(2, pHandles, FALSE, INFINITE); 

    switch (dwObjectWaitState) 
    { 
    case WAIT_ABANDONED: 
     TRACE(_T("SerialPortRxThreadFn : Owner thread terminated prematurely.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_ARENA_TRASHED, __WFILE__, __LINE__); 
     return ERROR_ARENA_TRASHED; 
     break; 

    case WAIT_TIMEOUT: 
     TRACE(_T("SerialPortRxThreadFn : The timeout is set to INFINITE; there should be no timeout. State is nonsignaled.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), WAIT_TIMEOUT, __WFILE__, __LINE__); 
     return WAIT_TIMEOUT; 
     break; 

    case WAIT_FAILED: 
     TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
     return ::GetLastError(); 
     break; 

    case WAIT_OBJECT_0:    // thread exit event signalled 
     bContinue = FALSE; 

     if (!::ResetEvent(pHandles[0])) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Failed to reset the serial port thread exit event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 
     break; 

    case WAIT_OBJECT_0 + 1:   // OVERLAPPED structure event signalled 
     // Read data from serial port. 
     if (!::ReadFile(s_hSerialPort, pBuf, RX_BUF_SIZE, &dwWritten, &s_ov)) // <- Set breakpoint here 
     { 
      TRACE(_T("SerialPortRxThreadFn : Call to ReadFile filed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 

     // Discontinue thread operation if there are no more bytes in the serial port receive buffer. 
     if (dwWritten == 0) // <- Or, set breakpoint here 
     { 
      bContinue = FALSE; 
     } 
     // Copy the received bytes to the thread-safe buffer. 
     else if (!s_pobjRxRingBuffer->Add(pBuf, dwWritten, TRUE)) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Failed to add bytes to ring buffer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_INSUFFICIENT_BUFFER, __WFILE__, __LINE__); 
      return ERROR_INSUFFICIENT_BUFFER; 
     } 
     else if (s_SpCallbackFn != NULL) 
     { 
      // Notify application of received data. 
      if ((dwRetVal = s_SpCallbackFn(s_pobjRxRingBuffer->ItemsInBuffer())) != ERROR_SUCCESS) 
      { 
       TRACE(_T("SerialPortRxThreadFn : Serial port callback function failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwRetVal, __WFILE__, __LINE__); 
       return dwRetVal; 
      } 
     } 

     if (!::ResetEvent(pHandles[1])) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Failed to reset the OVERLAPPED structure event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 
     break; 

    default: 
     // Do nothing. 
     break; 
    } 
} 

::CloseHandle(s_ov.hEvent); 

return ERROR_SUCCESS; 

Wenn ich meinen Haltepunkt auf der Linie gesetzt Aufruf ReadFile alles funktioniert, wie ich erwarte, und der PC wird in die Callback-Funktion. Wenn ich jedoch meinen Haltepunkt in der nächsten Zeile setze, wo dwWritten für Null ausgewertet wird, ist es Null, der Ausdruck wird als WAHR ausgewertet, und die Schleife wird beendet; der PC kommt nie zum Rückruf. Was mache ich falsch? Vielen Dank.

+1

Scheint so, als ob du viel mehr Arbeit machst, als du brauchst. Warum behandeln Sie serielle Daten asynchron in einem Thread? Warum lassen Sie nicht einfach den Thread auf I/O blockieren? – Jay

+1

Sie verlieren auch den Handle, Sie können nicht von der Funktion zurückkehren, ohne den Handle zu schließen, aber Sie tun dies unter Fehlerbedingungen. – doron

+1

Sie müssen das Ereignis pHandles [1] vor dem Aufruf von ReadFile zurücksetzen. –

Antwort

2

Ich bin kein Experte für die Win32-API, aber es klingt sicher wie ein Timing-Problem (das ist eine häufige Ursache von heisenbugs). Lassen Sie uns sagen, bis Sie ReadFile erhalten, gibt es keine Daten zu lesen. Wenn Sie in den Debugger einsteigen, kann es eine Pause für die Daten geben, so dass es erfolgreich ist, wenn Sie fortfahren/über ReadFile gehen.

Es gibt viele andere Dinge als die Ankunft von Daten, die das Ereignis auslösen könnten. Vielleicht möchten Sie Ihre dwEventMask überprüfen, ob meine Hypothese wahr ist.

+0

Alles was du gesagt hast ist wahr, aber der Vorschlag ist schlecht. Die Verwendung von 'dwEventMask' ist der falsche Weg, um herauszufinden, ob Daten empfangen wurden. Der richtige Weg ist, 'SetCommTimeouts' und' ReadFile' aufzurufen, so dass das Lesen abgeschlossen ist, sobald sich Daten im Kernel-Empfangspuffer befinden. 'WaitCommEvent' sagt Ihnen andere Dinge, wie den Zustand der Flusskontrollstifte. –

+0

@Ben Vielleicht war ich nicht klar. Ich meinte nicht, dass die Handhabung auf "dwEventMask" basieren sollte. Ich meinte, dass "dwEventMask" untersucht werden sollte, um festzustellen, ob meine Hypothese wahr ist. – NPE

+0

Vor dem Start des Threads ist 'EV_RXCHAR' das einzige Flag, das ich in meinem Aufruf von' SetCommMask' gesetzt habe.Gibt es noch andere Flags, die 'WaitCommEvent' abrufen kann? Ich dachte, dass es nur Flags abrufen würde, die in der Maske festgelegt sind. –

0

Die Dokumentation von WaiCommEvent besagt, dass Sie nach der Verwendung einer Wartefunktion (wie WaitForMultipleEObjects (...)) die Funktion GetOverlappedResult (...) verwenden, um die Ergebnisse Ihrer Operation abzurufen. Es sollte keine Read \ Write-Datei (...) geben.

+0

Sie können die empfangenen Bytes nicht ohne Aufruf von 'ReadFile' abrufen. Tatsächlich wird 'ReadFile' benötigt und' WaitCommEvent' ist es nicht. –

+0

@Ben Voigt: Ich denke du liegst falsch. Wenn Sie die Dokumentation zu WaitCommEvent lesen, können Sie lesen, dass ein Flag namens EV_RXCHAR für den EventMask-Parameter vorhanden ist. Und ReadFile ist Wahl keine Notwendigkeit. –

+0

Ich habe diese Funktionen schon einmal benutzt. Vertrauen Sie mir, Sie können im Überlappungsmodus mit 'ReadFile' (oder' ReadFileEx', was ich bevorzuge) und keinem 'WaitCommEvent' lesen. 'ReadFile' verwendet ein Kernel-Ereignis in seinem OVERLAPPED-Parameter, auf den Sie warten können. 'WaitCommEvent' kann Ihnen sagen, wenn an der seriellen Schnittstelle Aktivität auftritt, aber es gibt Ihnen nicht die tatsächlichen Daten. –

1

Sehr schmerzhaft, diesen Code zu sehen, einige davon geschrieben. Die Ausführlichkeit ist am besten in der Klassenbibliothek von jemand anderem zu finden. Ein paar Rote Flaggen. Sie nehmen an, dass die Beendigung von WaitCommEvent() bedeutet, dass Sie ReadFile() aufrufen können. Normalerweise ist die von Ihnen verwendete Ereignismaske nicht sichtbar, aber es gibt viele andere Gründe, warum der serielle Port Ihnen etwas mitteilen möchte. Ein weiteres Problem ist, dass WaitCommEvent möglicherweise sofort beendet wird. Es ist nicht ungewöhnlich, etwas im Empfangspuffer verfügbar.

Stehlen Sie diesen Code von irgendwo, es ist harter Code. Es wurde getan.

+0

Vor dem Start des Threads ist 'EV_RXCHAR' das einzige Flag, das ich in meinem Aufruf von' SetCommMask' gesetzt habe. Leider ist das Ausleihen von Code von außerhalb des Unternehmens in diesem Fall keine Option. Ich kann Beispiele sehen, ect., Aber der ganze Code muss von mir oder jemand anderem in der Firma geschrieben werden. –

0

Sie benötigen keine Kommunikationsereignisse, um Daten asynchron zu lesen. Rufen Sie einfach ReadFile, erhalten Sie den "Fehler" ERROR_IO_PENDING, und wenn Daten eintreffen wird das Ereignis signalisiert und Sie können dann die Anzahl der Bytes GetOverlappedResult erhalten, die Daten werden in den Puffer, den Sie ursprünglich an ReadFile geliefert.

Verwandte Themen