0

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 );
dsolimano
  • 8,870
  • 3
  • 48
  • 63
Jim Fell
  • 13,750
  • 36
  • 127
  • 202
  • You should never cast a function pointer like that (unless you're absolutely certain it is a good cast). You loose function signature checks. – engf-010 Jan 13 '11 at 00:16
  • I know that Microsoft has come up with all sorts of obfuscated ways of making and casting pointers, but in the end a pointer is a pointer is a pointer, a 32-bit (or 64-bit) atomic value representing a memory location address. I make sure to cast it back before using it; the threads started in here (and other places) work fine doing this. – Jim Fell Jan 13 '11 at 13:55
  • @Jim Fell: Every function has (besides a name and parameters) a calling covention (like _cdecl ,stdcall ,thiscall) and when you cast to a pointer with a different calling-convention ,there is no way to catch that error. You most likely get some kind of runtime-error and you may end up spending many hours ,sometimes day to find the mistake. – engf-010 Jan 13 '11 at 17:12
  • All that just for simple rs232 communications? That code is begging to be rewritten – Tim Jan 18 '11 at 19:47
  • You're almost never checking result of the functions you are calling, or is it because you cleaned up the code to make in lighter for publishing? – pagra Jan 18 '11 at 20:03
  • @patriice: Yes, as stated in my post, I removed all the error-handling to make it simpler. – Jim Fell Jan 18 '11 at 21:19

3 Answers3

1

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 . . .
}
zdan
  • 28,667
  • 7
  • 60
  • 71
1

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.

uray
  • 11,254
  • 13
  • 54
  • 74
  • Hi, uray. I accepted your answer because it's what finally got me on the right track. As you can see, it's pretty close to the final solution that I posted in my OP. – Jim Fell Jan 20 '11 at 16:43
0

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)?

engf-010
  • 3,980
  • 1
  • 14
  • 25