🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.nativeapi
Article #6256

Problem with serial communications

#6256
From: Martin Nijhoff
Date: Tue, 31 May 2005 09:38
198 lines
5696 bytes
Some time ago I wrote a serial port component using overlapped I/O. The
component creates a thread to monitor the serial port an trigger some event
handlers.

The component works fine on both Win98 and WinXP, that is, most of the time.
Sometimes an error occurs while writing to the serial port, in which case
the WriteFile() fails and GetLastError() returns ERROR_OPERATION_ABORTED
(995).

Here is the code for the thread:


#define EV_ALL 0x01FF

class TCOMStatThread : public TThread
{
  typedef TThread inherited;

  private:
    TCustomCOMPort *Port;
    OVERLAPPED Overlapped;
    HANDLE TerminateEvent;
    DWORD dwParam;

    void __fastcall HandleCommEvent (DWORD EventMask);
    void __fastcall DoOnDataAvailable ();
    void __fastcall DoOnError ();

  protected:
    void __fastcall Execute ();

  public:
    __fastcall TCOMStatThread (TCustomCOMPort *APort, bool Suspended);
    __fastcall ~TCOMStatThread ();
    void __fastcall Terminate ();
};

__fastcall TCOMStatThread::TCOMStatThread (TCustomCOMPort *APort, bool
Suspended) : TThread(Suspended)
{
  Port = APort;
  FreeOnTerminate = true;
  Overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
  TerminateEvent = CreateEvent(NULL, true, false, NULL);
}

__fastcall TCOMStatThread::~TCOMStatThread ()
{
  CloseHandle(Overlapped.hEvent);
  CloseHandle(TerminateEvent);
}

void __fastcall TCOMStatThread::Execute ()
{
  HANDLE EventHandles[2] = {Overlapped.hEvent, TerminateEvent};
  DWORD EventMask, ByteCount;

  SetCommMask(Port->Handle, EV_ALL);

  while (!Terminated)
  {
    if (WaitCommEvent(Port->Handle, &EventMask, &Overlapped))
      HandleCommEvent(EventMask);
    else
    {
      if (GetLastError() == ERROR_IO_PENDING)
      {
        switch (WaitForMultipleObjects(2, EventHandles, false, INFINITE))
        {
          case WAIT_OBJECT_0:  // Overlapped I/O completed.
            if (GetOverlappedResult(Port->Handle, &Overlapped, &ByteCount,
false))
              HandleCommEvent(EventMask);
            break;

          case WAIT_OBJECT_0 + 1:  // Termination signalled.
            break;
        }
      }
    }
  }
}

void __fastcall TCOMStatThread::HandleCommEvent (DWORD EventMask)
{
  if (EventMask & (EV_TXEMPTY | EV_RXCHAR | EV_ERR))
  {
    COMSTAT Status;
    DWORD Errors;

    if (!ClearCommError(Port->Handle, &Errors, &Status))
      return;

    if (Errors != 0 && Port->OnError != NULL)
    {
      dwParam = Errors;
      Synchronize(DoOnError);
    }

    if (Status.cbInQue > 0 && Port->OnDataAvailable != NULL)
    {
      dwParam = Status.cbInQue;
      Synchronize(DoOnDataAvailable);
    }
  }

  if (EventMask & EV_RXFLAG)
  {
    ...
  }

  if (EventMask & (EV_BREAK | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
  {
    ...
  }
}

void __fastcall TCOMStatThread::DoOnDataAvailable ()
{
  Port->OnDataAvailable(Port, dwParam);
}

void __fastcall TCOMStatThread::DoOnError ()
{
  Port->OnError(Port, dwParam);
}

void __fastcall TCOMStatThread::Terminate ()
{
  inherited::Terminate();
  SetEvent(TerminateEvent);
}


Here is the code for reading from and writing to the serial port:


DWORD __fastcall TCustomCOMPort::Read (void *Buffer, DWORD Size)
{
  DWORD BytesRead;

  if (FHandle == INVALID_HANDLE_VALUE)
    throw Exception(SPortClosed);

  if (!ReadFile(FHandle, Buffer, Size, &BytesRead, &osRead))
  {
    if (GetLastError() == ERROR_IO_PENDING)
      WaitForAsync(osRead, BytesRead);
  }

  return(BytesRead);
}

DWORD __fastcall TCustomCOMPort::Write (void *Buffer, DWORD Size)
{
  DWORD BytesWritten;

  if (FHandle == INVALID_HANDLE_VALUE)
    throw Exception(SPortClosed);

  if (!WriteFile(FHandle, Buffer, Size, &BytesWritten, &osWrite))
  {
    if (GetLastError() == ERROR_IO_PENDING)
      WaitForAsync(osWrite, BytesWritten);
  }

  return(BytesWritten);
}

void __fastcall TCustomCOMPort::WaitForAsync (OVERLAPPED &Overlapped, DWORD
&ByteCount)
{
  switch (WaitForSingleObject(Overlapped.hEvent, INFINITE))
  {
    case WAIT_FAILED:
      ByteCount = 0;
      break;

    default:
    case WAIT_OBJECT_0:
      if (!GetOverlappedResult(FHandle, &Overlapped, &ByteCount, false))
        ByteCount  = 0;
  }
}


I read somewhere that WaitCommEvent() doesn't report all errors. For EV_ERR,
only line-status errors are reported (CE_FRAME, CE_OVERRUN and CE_RXPARITY).
Other errors, like CE_RXOVER and CE_TXFULL, go unnoticed. I think this is
part of the problem, but how do I fix it?

Should I forget about WaitCommEvent() and simply call ClearCommError() in
the thread's main loop? This would cause a big load on the processor, right?

Any help would be greatly appreciated.


Martin
Technical Details
Message-ID: <429c30e1$1@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!feeder.enertel.nl!nntpfeed-01.ops.asmr-01.energis-idc.net!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mail

Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,ab642e2ba4263114,start
X-Google-Attributes: gid10cc67,public
X-Google-Language: ENGLISH,ASCII-7-bit
From: "Martin Nijhoff" <dont@mail.me>
Newsgroups: borland.public.cppbuilder.nativeapi
Subject: Problem with serial communications
Date: Tue, 31 May 2005 11:38:55 +0200
Lines: 200
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2800.1409
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1409
NNTP-Posting-Host: 80.100.129.156
Message-ID: <429c30e1$1@newsgroups.borland.com>
X-Trace: newsgroups.borland.com 1117532385 80.100.129.156 (31 May 2005 02:39:45 -0700)
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!feeder.enertel.nl!nntpfeed-01.ops.asmr-01.energis-idc.net!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mail