Question

My application is not properly receiving data from the COM port. This used to work. I don't know what happened. I know that the proper data is being sent/received over the line because I can see it on my protocol analyzer.

The PC gets into the WAIT_OBJECT_0 + 1 state, but the buffer contents are always zero. I know this is a lot, but if anyone can point out what I'm doing wrong, I'd really appreciate it. I can add/remove details as requested. Thanks.

EDIT: Additional Information

I have been able to verify that the PC makes the call to ReadFileEx, and it "succeeds." However, the PC never gets into FileIOCompletionRoutine. Any ideas? (I removed the error-handling from the code to make life simpler.) Also, from what I read on the MSDN website, it looks like FileIOCompletionRoutine will get called asynchronously in its own thread. Is that correct? Thanks.

EDIT: Final Solution

This is what I came up with. Obviously, the initialization and error-handling code is not here. We can't make things too easy. :)

// Load event handles.
pHandles[0] = s_hSerialPortRxThreadExitEvent;
// OVERLAPPED structure event handle is loaded in loop.

while ( blContinue )
{
    // Wait for a communications event.
    if ( !::WaitCommEvent( s_hSerialPort, &dwEventMask, &s_ov ) )
    {
        if ( ::GetLastError() != ERROR_IO_PENDING )
        {
            blContinue = FALSE;
            continue;
        }
        else if ( ::WaitForSingleObject( pHandles[0], 0 ) == WAIT_OBJECT_0 )
        {
            // The thread-exit event has been signaled.  Get out of here.
            blContinue = FALSE;
            continue;
        }
    }
    else
    {
        // Load OVERLAPPED structure event handle.
        pHandles[1] = s_ov.hEvent;
    }

    if ( dwEventMask & EV_RXCHAR )
    {
        if ( !::ReadFile( s_hSerialPort, pBuf, RX_BUF_SIZE, NULL, &s_ov ) )
        {
            if ( ::GetLastError() == ERROR_IO_PENDING )
            {
                // Wait for events.
                dwObjectWaitState = ::WaitForMultipleObjects( 2, pHandles, FALSE, INFINITE );

                // Switch on event.
                switch ( dwObjectWaitState )
                {
                case WAIT_OBJECT_0:             // thread exit event signaled
                    blContinue = FALSE;
                    continue;

                case WAIT_OBJECT_0 + 1:         // OVERLAPPED structure event signalled
                    // Reset event first to mitigate underrun condition.
                    if ( !::ResetEvent( pHandles[1] ) )
                    {
                        blContinue = FALSE;
                        continue;
                    }

                    // Get the OVERLAPPED result.
                    if ( !::GetOverlappedResult( s_hSerialPort, &s_ov, &dwBytesRead, FALSE ) )
                    {
                        blContinue = FALSE;
                        continue;
                    }
                    break;

                default:                        // Error
                    blContinue = FALSE;
                    continue;
                }
            }
        }
        else if ( !::GetOverlappedResult( s_hSerialPort, &s_ov, &dwBytesRead, FALSE ) )
        {
            blContinue = FALSE;
            continue;
        }

        // If bytes were read...
        if ( dwBytesRead > 0 )
        {
            // Copy received data from local buffer to thread-safe serial port buffer.
            ::EnterCriticalSection( &s_csRxRingBuffer );
            blSuccess = s_pobjRxRingBuffer->Add( pBuf, dwBytesRead, TRUE );
            ::LeaveCriticalSection( &s_csRxRingBuffer );

            if ( !blSuccess )
            {
                blContinue = FALSE;
                continue;
            }

            // Set the received data event.
            if ( !::SetEvent( s_phEventIds[RECEIVE_EVENT] ) )
            {
                blContinue = FALSE;
                continue;
            }
        }
    }

    if ( dwEventMask & EV_TXEMPTY )
    {
        // Set the transmit complete event.
        if ( !::SetEvent( s_phEventIds[TRANSMIT_EVENT] ) )
        {
            blContinue = FALSE;
            continue;
        }
    }

} // end while ( blContinue );
Was it helpful?

Solution

I don't know if I see your code wrong, but I see that you are setting up event, and waiting for the event without telling the O/S what are you waiting for, you should do ReadFileEx() first to tell O/S you are waiting for event when there's data in the buffer to read, then you do WFSO()/WFMO(), this what I do on my Serial port library (on receiver thread):

do
{
    byteRead = 0;
    readBuffer = 0;
    if(!::ReadFile(this->commHandle,&readBuffer,
                   1,NULL,&this->readOverlapIO))
    {
         if(GetLastError() == ERROR_IO_PENDING)
         {
             if(::WaitForSingleObject(this->readOverlapIO.hEvent,INFINITE) == WAIT_OBJECT_0)
             {
                 if(!::GetOverlappedResult(this->commHandle,&this->readOverlapIO,&byteRead,FALSE))
                 {
                     byteRead = 0;
                 }
             }
         }
    }
    else
    {
         if(!::GetOverlappedResult(this->commHandle,&this->readOverlapIO,&byteRead,FALSE))
         {
              byteRead = 0;
         }
    }
    if(byteRead > 0)
    {
          totalByteRead += this->ringBuffer.push(readBuffer,1);
    }
}while(byteRead);

I use completion event using event signal here, you can change it to completion function if you wanted to.

OTHER TIPS

Also, from what I read on the MSDN website, it looks like FileIOCompletionRoutine will get called asynchronously in its own thread. Is that correct?

No, that is not correct. The completion routine gets called in the context of the thread in which it the ReadFileEx called is made. When it is ready to run, it is queued until the thread is in an "alertable wait state". That typically happens when you call one of the Wait* functions. Furthermore, from the MSDN for ReadFileEx, it states:

The ReadFileEx function ignores the OVERLAPPED structure's hEvent member. An application is free to use that member for its own purposes in the context of a ReadFileEx call. ReadFileEx signals completion of its read operation by calling, or queuing a call to, the completion routine pointed to by lpCompletionRoutine, so it does not need an event handle.

So the event you have created has no effect. Also, the reads will not be processed until ReadFileEx is called, so you have to reverse the order with your wait. What you should be doing in your reading loop is something like this (in Pseudocode):

while(continue)
{
    ReadFileEx();
    WaitForSingleObject(s_hSerialPortRxThreadExitEvent);
    . . . etc . . .
}

if youre sure that something (should) have been received ,then may it is the double usage of &dwWritten. Try using two separate variables (one for ReadFile and another for GetOverlappedResult). Check both of them of course.

Also ,Have you initialized the full overlapped structure to 0's (beffore assigning the event to it)?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top