CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 6 of 6
  1. #1
    Join Date
    Oct 1999
    Location
    UK
    Posts
    44

    Serial comms using WriteFile locks up. Please help

    I have written some C code to perform serial comms. The receive code is in a
    separate thread, and handles received bytes one at a time. CD_TxPacket should
    send all bytes at once, using WriteFile. Our protocol is simplex, sending a
    request and then waiting for a response.

    I find that the code locks up on the WriteFile function after an unspecified
    number of bytes have been sent (sometime <100, other times >900,000). The
    message handling continues to work (scroll, close, etc.), but the transmit
    just stalls forever.

    There is no handshaking required.

    Any suggestions would be welcome.

    TIA, Mark


    /* Send the number of bytes supplied to the port, return number of bytes actually sent */
    WORD /* Number of bytes successfully sent */
    CD_TxPacket(
    char* pData, /* Pointer to start of data */
    WORD nDataSize /* Number of bytes to send */
    )
    {
    HANDLE hPort;
    COMSTAT ComStat;
    DWORD BytesWritten;
    DWORD dwErrorFlags;

    /* For information, baud rate is 38400, no parity, 8 bit, 1 stop.
    Timeouts are:
    CommTimeOuts.ReadIntervalTimeout = 2;
    CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
    CommTimeOuts.ReadTotalTimeoutConstant = 0;
    CommTimeOuts.WriteTotalTimeoutMultiplier = 1;
    CommTimeOuts.WriteTotalTimeoutConstant = 30000;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDtrControl = DTR_CONTROL_ENABLE;
    dcb.fOutxCtsFlow = FALSE;
    dcb.fRtsControl = RTS_CONTROL_ENABLE;
    dcb.fInX = dcb.fOutX = FALSE;
    */
    EnterCriticalSection( &CriticalSect ); // I don't know whether I need these

    if ( !WriteFile( hPort, pData, nDataSize, &BytesWritten, NULL ) )
    {
    /* WriteFile error */
    ClearCommError( hPort, &dwErrorFlags, &ComStat );
    }
    LeaveCriticalSection( &CriticalSect );
    return (WORD)BytesWritten;
    }

    DWORD FAR PASCAL CommWatchThreadProc( void )
    {
    #define NUM_READSTAT_HANDLES 2

    BYTE CommErr;
    BYTE DataByte;
    BOOLEAN bThreadDone = FALSE; /* Flag to tell thread when to shut down */
    DWORD dwCommEvent; /* result from WaitCommEvent */
    DWORD dwLength; /* bytes actually read */
    DWORD dwWaitResult; /* result from WaitForMultipleObjects */
    HANDLE hArray[ NUM_READSTAT_HANDLES ]; /* Array of event handles used by WaitForMultipleObjects */

    /* Set up the array of events used by WaitForMultipleObjects */
    hArray[ 0 ] = hRxTxChangeEvent; /* Rx/Tx control change event, generated by SetEvent in CD_RxTxControl */
    hArray[ 1 ] = hThreadExitEvent; /* Exit thread event, generated by SetEvent in CD_DriverTerm */

    ASSERT0_RET( SetCommMask( hPort, 0 ), FALSE );

    while ( !bThreadDone )
    {
    dwWaitResult = WaitForMultipleObjects( NUM_READSTAT_HANDLES, hArray, FALSE, ( RxTxState != C_RXEN ) ? 10000 : 0 );
    switch ( dwWaitResult )
    {
    case WAIT_OBJECT_0: /* Signal from main thread to say Rx or Tx enabled to break out of wait state */
    ResetEvent( hRxTxChangeEvent ); /* Reset Rx/Tx control change event for next time */
    break;

    case WAIT_OBJECT_0 + 1: /* Exit thread event, generated by SetEvent in CD_DriverTerm */
    bThreadDone = TRUE;
    break;
    }

    if ( ( RxTxState == C_TXEN ) && bFrameQueued ) /* If Tx enabled and data is queued */
    {
    EnterCriticalSection( &CriticalSect );
    if ( WaitCommEvent( hPort, &dwCommEvent, NULL ) ) /* Looking for EV_TXEMPTY or 0 */
    {
    CommErr = CheckCommsErrors();
    if ( ( dwCommEvent & EV_TXEMPTY ) == EV_TXEMPTY )
    {
    pLink->bFrameSent = TRUE;
    CD_TxEndCallback(); /* Tell main thread that all bytes have been sent */
    }
    }
    else /* WaitCommEvent error */
    {
    CommErr = CheckCommsErrors();
    }
    LeaveCriticalSection( &CriticalSect );
    }
    else if ( RxTxState == C_RXEN )
    {
    EnterCriticalSection( &CriticalSect );
    if ( ReadFile( hPort, &DataByte, BYTES_TO_READ, &dwLength, NULL ) ) /* Times out! */
    {
    LeaveCriticalSection( &CriticalSect );
    if ( RxTxState == C_RXEN ) /* Still expecting to receive data? */
    {
    if ( ( CommErr = CheckCommsErrors() != C_NOERROR )
    {
    CP_PutRxByte( 0, CommErr );
    }
    else if ( dwLength == BYTES_TO_READ )
    {
    CP_PutRxByte( DataByte, C_NOERROR );
    }
    else /* Failed to read byte within timeout period */
    {
    CP_PutRxByte( 0, C_FRAMETIMEOUT );
    }
    }
    }
    else /* ReadFile error */
    {
    LeaveCriticalSection( &CriticalSect );
    if ( ( CommErr = CheckCommsErrors() ) != C_NOERROR )
    {
    CP_PutRxByte( 0, CommErr );
    }
    }
    }
    }
    return TRUE;
    }





    Mark
    Regards

    Mark Rivers-Moore

  2. #2
    Guest

    Re: Serial comms using WriteFile locks up. Please help

    Mark,

    Did you ever solve this problem. I am having the same exact problem. WriteFile to com port locks up but only when I run my code on Win95 machines. never locks up on WinNT. I'm sure I have my DCB and COMMTIMEOUTS settings correct. I have seen other post on the net on this issue, but didn't see anything on msdn.

    Thanks, Sean



  3. #3
    Join Date
    Oct 1999
    Location
    UK
    Posts
    44

    Re: Serial comms using WriteFile locks up. Please help

    No. I had to give up trying and proceed with the rest of the project using a third party driver, but ultimately I need to use the Windows API, otherwise we have to pay $190 per copy when I send out the programme.

    If you do find a solution, please let me know too.

    Regards
    Mark

    Mark
    Regards

    Mark Rivers-Moore

  4. #4
    Join Date
    Feb 2000
    Posts
    23

    Re: Serial comms using WriteFile locks up. Please help

    Hi Mark,
    I mostly use a third part library (Greenleaf Comm++) but I did notice that you are not using an overlapped * in your WriteFile(..). I have always used FILE_FLAG_OVERLAPPED in the OpenFile and a pointer to on OVERLAPPED structure in the write. I think ?? you have to with async communications. Its a little tricky because write will return immediately with an error. You call get last error and get something back like 'ERROR_IO_PENDING' Then just look for the event to be signaled in the overlapped structure. If I remember you also have to reset the pointer afterwards.
    Hope this helps,
    Bob



  5. #5
    Join Date
    Oct 1999
    Location
    UK
    Posts
    44

    Re: Serial comms using WriteFile locks up. Please help

    I haven't looked at this problem for several months now, but when I was, I tried all sorts of combinations with and without overlapped I/O. Since my code can operate on an alternate talk/listen cycle, I got to the code shown in my original post as perhaps the simplest form. The code seemed to work perfectly up to the point that the call to WriteFile(...) failed to return after many successful calls.

    Since Sean re-awoke the thread, after 3 months :-), I have just copied the original message to microsoft.public.vc.language where I am (hopefully) guaranteed a response from a Microsoft Support Professional within 72 hours through an 'Online Developer's Support' pilot programme. They certainly sorted out a VB6 problem I had using callbacks from this comms code in my DLL, well within 3 days.

    If I get a useful result there, I'll post it here too.

    Regards
    Mark

    Mark
    Regards

    Mark Rivers-Moore

  6. #6
    Join Date
    Oct 1999
    Location
    UK
    Posts
    44

    Re: Serial comms using WriteFile locks up. Please help

    I believe I have finally discovered the cause of the lockup! :-)


    To recap, my code uses non-overlapped serial I/O, but uses two threads, with reads done in one thread and writes from the main application thread. I have been testing my comms with both my Master app and my Slave app running on the same PC with COM1 looped to COM2.

    I am tied to reading and writing only one byte at a time, because the same source files are also compiled to run on other platforms. There is only one 'driver-level' file to interface between the hardware and the protocol layer.


    I found the answer in the depths of the text of http://msdn.microsoft.com/library/te...sdn_serial.htm.

    The problem revealed: "There are two interesting side effects of SetCommMask and WaitCommEvent. First, if the communications port is open for nonoverlapped operation, WaitCommEvent will be blocked until an event occurs. If another thread calls SetCommMask to set a new event mask, that thread will be blocked on the call to SetCommMask. The reason is that the original call to WaitCommEvent in the first thread is still executing. The call to SetCommMask blocks the thread until the WaitCommEvent function returns in the first thread. This side effect is universal for ports open for nonoverlapped I/O. If a thread is blocked on any communications function and another thread calls a communications function, the second thread is blocked until the communications function returns in the first thread."

    My code was obviously getting into the ReadFile function in one of the programs, but without timeouts in use, it stayed there forever, waiting for a byte to be sent from the other program using WriteFile, but this was blocked by the ReadFile on the other port.

    I think this is a very subtly hidden bug, which is rather poorly highlighted in the documentation.

    I have cured the problem by using ReadTotalTimeoutConstant to break out of ReadFile regularly, giving other threads and ports a chance to do something too.

    I hope others may find this discovery useful.

    Regards


    Mark
    Regards

    Mark Rivers-Moore

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured