CSerialPort类解析

来源:互联网 发布:淘宝小卖家如何推广 编辑:程序博客网 时间:2024/05/17 04:19

CSerialPort类的功能及成员函数介绍

CSerialPort类是免费提供的串口累,Codeguru是一个非常不错的源代码网站

CSerialPort类支持线连接(非MODEM)的串口编程操作。

CSerialPort类是基于多线程的,其工作流程如下:首先设置好串口参数,再开启串口检测工作线程,串口检测工作线程检测到串口接收到的数据、流控制事件或其他串口事件后,就以消息方式通知主程序,激发消息处理函数来进行数据处理,这是对接受数据而言的,发送数据可直接向串口发送。

CSerialPort类定义的消息如表

消息名称

消息号

功能说明

WM_COMM_BREAK_DETECTED

WM_USER+1

检测到输入中断

WM_COMM_CTS_DETECTED

WM_USER+2

检测到CTS(清除发送)信号状态改变

WM_COMM_DSR_DETECTED

WM_USER+3

检测到DSR(数据设备准备就绪)信号状态改变

WM_COMM_ERR_DETECTED

WM_USER+4

发生线状态错误(包括CE_FRAMECE_OVERRUN,和CE_RXPARITY

WM_COMM_RING_DETECTED

WM_USER+5

检测到响铃指示信号

WM_COMM_RLSD_DETECTED

WM_USER+6

检测到RLSD(接收线信号)状态改变

WM_COMM_RXCHAR

WM_USER+7

接收到一个字符并已放入接受缓冲区

WM_COMM_RXFLAG_DETECTED

WM_USER+8

检测到接受到字符(该字符已放入接受缓冲区)事件

WM_COMM_TXEMPTY_DETECTED

WM_USER+9

检测到发送缓冲区最后一个字符已经被发送

介绍几个经常用到的函数:
1、串口初始化函数InitPort
BOOL CSerialPort::InitPort(CWnd *pPortOwner,    // the owner (CWnd) of the port (receives message)                            UINT  portnr,        // portnumber (1..4)                            UINT  baud,            // baudrate                            char  parity,        // parity                            UINT  databits,        // databits                            UINT  stopbits,        // stopbits                            DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc                            UINT  writebuffersize)    // size to the writebuffer     assert(portnr > 0 && portnr < 5);     assert(pPortOwner != NULL);      // if the thread is alive: Kill     if (m_bThreadAlive)     {         do         {             SetEvent(m_hShutdownEvent);         }         while (m_bThreadAlive);         TRACE("Thread ended\n");     }      // create events     if (m_ov.hEvent != NULL)         ResetEvent(m_ov.hEvent);     m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);      if (m_hWriteEvent != NULL)         ResetEvent(m_hWriteEvent);     m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);      if (m_hShutdownEvent != NULL)         ResetEvent(m_hShutdownEvent);     m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);      // initialize the event objects     m_hEventArray[0] = m_hShutdownEvent;    // highest priority     m_hEventArray[1] = m_ov.hEvent;     m_hEventArray[2] = m_hWriteEvent;      // initialize critical section     InitializeCriticalSection(&m_csCommunicationSync);      // set buffersize for writing and save the owner     m_pOwner = pPortOwner;      if (m_szWriteBuffer != NULL)         delete [] m_szWriteBuffer;     m_szWriteBuffer = new char[writebuffersize];      m_nPortNr = portnr;      m_nWriteBufferSize = writebuffersize;     m_dwCommEvents = dwCommEvents;      BOOL bResult = FALSE;     char *szPort = new char[50];     char *szBaud = new char[50];      // now it critical!     EnterCriticalSection(&m_csCommunicationSync);      // if the port is already opened: close it     if (m_hComm != NULL)     {         CloseHandle(m_hComm);         m_hComm = NULL;     }      // prepare port strings     sprintf(szPort, "COM%d", portnr);     sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);      // get a handle to the port     m_hComm = CreateFile(szPort,                        // communication port string (COMX)                          GENERIC_READ | GENERIC_WRITE,    // read/write types                          0,                                // comm devices must be opened with exclusive access                          NULL,                            // no security attributes                          OPEN_EXISTING,                    // comm devices must use OPEN_EXISTING                          FILE_FLAG_OVERLAPPED,            // Async I/O                          0);                            // template must be 0 for comm devices      if (m_hComm == INVALID_HANDLE_VALUE)     {         // port not found         delete [] szPort;         delete [] szBaud;          return FALSE;     }      // set the timeout values     m_CommTimeouts.ReadIntervalTimeout = 1000    m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000    m_CommTimeouts.ReadTotalTimeoutConstant = 1000    m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000    m_CommTimeouts.WriteTotalTimeoutConstant = 1000     // configure     if (SetCommTimeouts(m_hComm, &m_CommTimeouts))     {         if (SetCommMask(m_hComm, dwCommEvents))         {             if (GetCommState(m_hComm, &m_dcb))             {                 m_dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS bit high!                 if (BuildCommDCB(szBaud, &m_dcb))                 {                     if (SetCommState(m_hComm, &m_dcb))                         ; // normal operation... continue                     else                         ProcessErrorMessage("SetCommState()");                 }                 else                     ProcessErrorMessage("BuildCommDCB()");             }             else                 ProcessErrorMessage("GetCommState()");         }         else             ProcessErrorMessage("SetCommMask()");     }     else         ProcessErrorMessage("SetCommTimeouts()");      delete [] szPort;     delete [] szBaud;      // flush the port     PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);      // release critical section     LeaveCriticalSection(&m_csCommunicationSync);      TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);      return TRUE; 

这个函数是用来初始化串口的,即设置串口的通信参数:需要打开的串口号、波特率、奇偶校验方式、数据位、停止位,这里还可 以用来进行事件的设定。

如果串口初始化成功,就返回TRUE,若串口被其他设备占用、不存在或存在其他股占,就返回FALSE,编程者可以在这儿提示串口操作是否成功。

如果在当前主串口调用这个函数,那么pPortOwner可用this指针表示,串口号在函数中做了限制,只能用1234四个串口号,而事实上在编程时可能用到更多串口号,可以通过通过注释掉本函数中的“assert(portur>0&&portnr<5)”语句取消对串口号的限制。

2、启动串口通信监测线程函数StartMonitoring()

串口初始化成功后,就可以调用BOOL StartMonitoring()来启动串口检测线程,线程启动成功,发挥TRUE

BOOL CSerialPort::StartMonitoring() 

if (!(m_Thread = AfxBeginThread(CommThread, this))) 

return FALSE; 

TRACE("Thread started\n"); 

return TRUE; 

3、暂停或停止监测线程函数StopMonitoring()

该函数暂停或停止串口检测,要注意的是,调用该函数后,串口资源仍然被占用
// Suspend the comm thread 
// 
BOOL CSerialPort::StopMonitoring() 

     TRACE("Thread suspended\n"); 
     m_Thread->SuspendThread(); 
     return TRUE; 

4、关闭串口函数ClosePort()

该函数功能是关闭串口,释放串口资源,调用该函数后,如果要继续使用串口,还需要调用InitPort()函数

5、通过串口发送字符/写串口函数WriteToPort()

该函数完成写串口功能,即向串口发送字符。

// Write a string to the port 

void CSerialPort::WriteToPort(char *string) 

{

assert(m_hComm != 0); 

memset(m_szWriteBuffer, 0sizeof(m_szWriteBuffer)); 

strcpy(m_szWriteBuffer, string); 
 

// set event for write 

SetEvent(m_hWriteEvent); 


以上是常用的函数介绍,熟悉该类的使用后,可以仔细看看其他函数,对上面介绍的函数,在对串口资源的使用上要记住一下三点:

打开串口用调用InitPort()和StartMonitoring();关闭串口用StopMonitoring()和ClosePort()而且以上函数的调用顺序不能乱

通过串口发送字符调用函数WriteToPort()

接受串口收到的字符需要自己编写WM_COMM_RXCHAR消息处理函数,需要手工添加。

操作:

首先,需要操作一个串口,所以只需要定义1个类对象就可以了,如要操作多个串口,则要为每个串口均定一个类对象,这可以通过数据方式来实现,这里定义的类对象为m_SerialPort,再定义一个布尔变量m_bSerialPortOpened用来标志串口是否打开。

CserialPort类中有多个串口事件可以响应,在一般串口编程中,只需要处理WM_COMM_RXCHAR消息就可以了,该类所有的消息均需要人工添加消息处理函数,将处理函数名定义为OnComm(),首先在SerialPortTestDlg.h(头文件)中添加串口字符接受消息WM_COMM_RXCHAR(串口接受缓冲区内有一个字符)的响应函数声明:

//Generated message map funnctions 
//{{AFX_MSG(CSCportTestView) 
afx_msg long OnComm(WPARAM ch, LPARAM port); 
//}}AFX_MSG 
 

然后在,SerilPortTestDlg.cpp文件中进行WM_COMM_RXCHAR消息映射

BEGIN_MESSAE_MAP(CSerialPortTestDlg, CDialog)s 
//{{AFX_MSG_MAP(CSerialPortTestDlg) 
ON_MESSAGE(WM_COMM_RXCHAR, OnComm) 
//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 接着,在SerialPortTestDlg.cpp文件中加入函数OnComm()的实现,并在其中完成对节诶受到的字符的处理,将接收到的字符显示在接受编辑框中:

long CSerialPortTestDlg::OnComm(WPARAM ch, LPARAM port) 
    m_strEditReceiveMsg += ch; 
    UpdateData(FLASE);//将接收到的字符显示在接受编辑框中 
    returne 0

说明:WPARAMLPARAM类型是多态数据类型,在WIN32中为32位,支持多种数据类型,根据需要自动适应,这样,程序有很强的适应性,再次,我们可以分贝理解为charinteger类型数据,每当串口接受缓冲区内有一个字符时,就会产生一个WM_COMM_RXCHAR消息,除法OnComm()函数,这时就可以在函数中进行数据处理,所以,这个消息就是整个程序的源头。


虽然CSerialPort类是一个非常好的类,但毕竟只是集中了作者一个人的智慧和经验,他也有许多缺陷,

原类只能发送字符(ASCII文本)不能处理二进制发送(也就是不能发送0X00

该类不能很好的释放串口

存在内存泄漏

可以进行如下改进

改进一、ASCII文本和二进制数据发送方式兼容

CSerialPort类中只有一个发送函数WriteToPort()

// 
// Write a string to the port 
// 
void CSerialPort::WriteToPort(char *string) 
    assert(m_hComm != 0); 
 
    memset(m_szWriteBuffer, 0sizeof(m_szWriteBuffer)); 
    strcpy(m_szWriteBuffer, string); 
 
    // set event for write 
    SetEvent(m_hWriteEvent); 
调用上面的函数就只能用字符串方式,而c语言中,空字符(NULL,其中ASCII码值为0,即通常所说的十六禁止0x00字符),是串结束符,当检测到NULL字符后,就认为该字符串结束了,所以0x00字符以ASCII文本方式是不能从串口发送出去的,那么解决这一问题的方法就是用二进制发送,其实这里说的二进制,只不过是不以我们通常所说的“可见”或“能显示的字符”发送,比如,要发如下的一组值:
char chSend[5]={0x33,0x96,0x00,0x31,0xf1};

下面来对类做一些改进,解决这个问题,原理就是用字符数据来发送数据,并在发送时指定其长度,这样,数据没有发送完,发送过程就不会停止,CSerialPort类是用API函数编写的在,只要在WriteFile()函数中指定其实际要发送的长度,就可以将数据全部发送出去:

实现步骤如下:

1、SerialPort.h文件中为CSerialPort类添加一个整形public成员变量,:int m_nWriteSize;用于指定发送字符数据的长度

添加三个发送函数

h文件:
#ifndef __SERIALPORT_H__ #define __SERIALPORT_H__  #define WM_COMM_BREAK_DETECTED        WM_USER+1    // A break was detected on input. #define WM_COMM_CTS_DETECTED        WM_USER+2    // The CTS (clear-to-send) signal changed state.  #define WM_COMM_DSR_DETECTED        WM_USER+3    // The DSR (data-set-ready) signal changed state.  #define WM_COMM_ERR_DETECTED        WM_USER+4    // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.  #define WM_COMM_RING_DETECTED        WM_USER+5    // A ring indicator was detected.  #define WM_COMM_RLSD_DETECTED        WM_USER+6    // The RLSD (receive-line-signal-detect) signal changed state.  #define WM_COMM_RXCHAR                WM_USER+7    // A character was received and placed in the input buffer.  #define WM_COMM_RXFLAG_DETECTED        WM_USER+8    // The event character was received and placed in the input buffer.   #define WM_COMM_TXEMPTY_DETECTED    WM_USER+9    // The last character in the output buffer was sent.     class CSerialPort { public:     void ClosePort();     void WriteToPort(LPCTSTR string, int n);     void WriteToPort(LPCTSTR string);     void WriteToPort(char *string, int n);     CSerialPort();     virtual        ~CSerialPort();     BOOL        InitPort(CWnd *pPortOwner,    // the owner (CWnd) of the port (receives message)                          UINT  portnr,        // portnumber (1..4)                          UINT  baud,            // baudrate                          char  parity,        // parity                          UINT  databits,        // databits                          UINT  stopbits,        // stopbits                          DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc                          UINT  writebuffersize);    // size to the writebuffer      BOOL        StartMonitoring();     BOOL        RestartMonitoring();     BOOL        StopMonitoring();     DWORD        GetWriteBufferSize();     DWORD        GetCommEvents();     DCB            GetDCB();      void        WriteToPort(char *string);     int m_nWriteSize;  protected:     // protected memberfunctions     void        ProcessErrorMessage(char *ErrorText);     static UINT    CommThread(LPVOID pParam);     static void    ReceiveChar(CSerialPort *port, COMSTAT comstat);     static void    WriteChar(CSerialPort *port);      // thread     CWinThread            *m_Thread;      // synchronisation objects     CRITICAL_SECTION    m_csCommunicationSync;     BOOL                m_bThreadAlive;      // handles     HANDLE                m_hShutdownEvent;     HANDLE                m_hComm;     HANDLE                m_hWriteEvent;      // Event array.     // One element is used for each event. There are two event handles for each port.     // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).     // There is a general shutdown when the port is closed.     HANDLE                m_hEventArray[3];      // structures     OVERLAPPED            m_ov;     COMMTIMEOUTS        m_CommTimeouts;     DCB                    m_dcb;      // owner window     CWnd                *m_pOwner;      // misc     UINT                m_nPortNr;     char                *m_szWriteBuffer;     DWORD                m_dwCommEvents;     DWORD                m_nWriteBufferSize; };  #endif __SERIALPORT_H__ 

cpp文件:
CSerialPort.cpp /*  ** FILENAME CSerialPort.cpp  **  ** PURPOSE This class can read, write and watch one serial port.  ** It sends messages to its owner when something happends on the port  ** The class creates a thread for reading and writing so the main  ** program is not blocked.  **  ** CREATION DATE 15-09-1997  ** LAST MODIFICATION 12-11-1997  **  ** AUTHOR Remon Spekreijse  **  **  */   #include "stdafx.h"  #include "CSerialPort.h"  #include <assert.h>   //  // Constructor  //  CSerialPort::CSerialPort()  {      m_hComm = NULL;       // initialize overlapped structure members to zero      m_ov.Offset = 0;      m_ov.OffsetHigh = 0;       // create events      m_ov.hEvent = NULL;      m_hWriteEvent = NULL;      m_hShutdownEvent = NULL;       m_szWriteBuffer = NULL;       m_bThreadAlive = FALSE;       m_nWriteSize = 0;  }   //  // Delete dynamic memory  //  CSerialPort::~CSerialPort()  {      do      {          SetEvent(m_hShutdownEvent);      }     while (m_bThreadAlive);       TRACE("Thread ended\n");       delete [] m_szWriteBuffer;  }   //  // Initialize the port. This can be port 1 to 4.  //  BOOL CSerialPort::InitPort(CWnd *pPortOwner, // the owner (CWnd) of the port (receives message)                             UINT  portnr, // portnumber (1..4)                             UINT  baud, // baudrate                             char  parity, // parity                             UINT  databits, // databits                             UINT  stopbits, // stopbits                             DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc                             UINT  writebuffersize) // size to the writebuffer  {      assert(portnr > 0 && portnr < 5);      assert(pPortOwner != NULL);       // if the thread is alive: Kill      if (m_bThreadAlive)      {          do          {              SetEvent(m_hShutdownEvent);          }         while (m_bThreadAlive);          TRACE("Thread ended\n");      }       // create events      if (m_ov.hEvent != NULL)          ResetEvent(m_ov.hEvent);      else          m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);       if (m_hWriteEvent != NULL)          ResetEvent(m_hWriteEvent);      else          m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);       if (m_hShutdownEvent != NULL)          ResetEvent(m_hShutdownEvent);      else          m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);       // initialize the event objects      m_hEventArray[0] = m_hShutdownEvent; // highest priority      m_hEventArray[1] = m_ov.hEvent;      m_hEventArray[2] = m_hWriteEvent;       // initialize critical section      InitializeCriticalSection(&m_csCommunicationSync);       // set buffersize for writing and save the owner      m_pOwner = pPortOwner;       if (m_szWriteBuffer != NULL)          delete [] m_szWriteBuffer;      m_szWriteBuffer = new char[writebuffersize];       m_nPortNr = portnr;       m_nWriteBufferSize = writebuffersize;      m_dwCommEvents = dwCommEvents;       BOOL bResult = FALSE;      char *szPort = new char[50];      char *szBaud = new char[50];       // now it critical!      EnterCriticalSection(&m_csCommunicationSync);       // if the port is already opened: close it      if (m_hComm != NULL)      {          CloseHandle(m_hComm);          m_hComm = NULL;      }       // prepare port strings      sprintf(szPort, "COM%d", portnr);      sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);       // get a handle to the port      m_hComm = CreateFile(szPort, // communication port string (COMX)                           GENERIC_READ | GENERIC_WRITE, // read/write types                           0, // comm devices must be opened with exclusive access                           NULL, // no security attributes                           OPEN_EXISTING, // comm devices must use OPEN_EXISTING                           FILE_FLAG_OVERLAPPED, // Async I/O                           0); // template must be 0 for comm devices       if (m_hComm == INVALID_HANDLE_VALUE)      {          // port not found          delete [] szPort;          delete [] szBaud;           return FALSE;      }       // set the timeout values      m_CommTimeouts.ReadIntervalTimeout = 1000;      m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;      m_CommTimeouts.ReadTotalTimeoutConstant = 1000;      m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;      m_CommTimeouts.WriteTotalTimeoutConstant = 1000;       // configure      if (SetCommTimeouts(m_hComm, &m_CommTimeouts))      {          if (SetCommMask(m_hComm, dwCommEvents))          {              if (GetCommState(m_hComm, &m_dcb))              {                  m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!                  if (BuildCommDCB(szBaud, &m_dcb))                  {                      if (SetCommState(m_hComm, &m_dcb))                          ; // normal operation... continue                      else                          ProcessErrorMessage("SetCommState()");                  }                  else                      ProcessErrorMessage("BuildCommDCB()");              }              else                  ProcessErrorMessage("GetCommState()");          }          else              ProcessErrorMessage("SetCommMask()");      }      else          ProcessErrorMessage("SetCommTimeouts()");       delete [] szPort;      delete [] szBaud;       // flush the port      PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);       // release critical section      LeaveCriticalSection(&m_csCommunicationSync);       TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);       return TRUE;  }   //  //  The CommThread Function.  //  UINT CSerialPort::CommThread(LPVOID pParam)  {      // Cast the void pointer passed to the thread back to      // a pointer of CSerialPort class      CSerialPort *port = (CSerialPort *)pParam;       // Set the status variable in the dialog class to      // TRUE to indicate the thread is running.      port->m_bThreadAlive = TRUE;       // Misc. variables      DWORD BytesTransfered = 0;      DWORD Event = 0;      DWORD CommEvent = 0;      DWORD dwError = 0;      COMSTAT comstat;      BOOL  bResult = TRUE;       // Clear comm buffers at startup      if (port->m_hComm) // check if the port is opened          PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);       // begin forever loop.  This loop will run as long as the thread is alive.      for (;;)      {           // Make a call to WaitCommEvent().  This call will return immediatly          // because our port was created as an async port (FILE_FLAG_OVERLAPPED          // and an m_OverlappedStructerlapped structure specified).  This call will cause the          // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to          // be placed in a non-signeled state if there are no bytes available to be read,          // or to a signeled state if there are bytes available.  If this event handle          // is set to the non-signeled state, it will be set to signeled when a          // character arrives at the port.           // we do this for each port!           bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);           if (!bResult)          {              // If WaitCommEvent() returns FALSE, process the last error to determin              // the reason..              switch (dwError = GetLastError())              {              case ERROR_IO_PENDING:              {                  // This is a normal return value if there are no bytes                  // to read at the port.                  // Do nothing and continue                  break;              }              case 87:              {                  // Under Windows NT, this value is returned for some reason.                  // I have not investigated why, but it is also a valid reply                  // Also do nothing and continue.                  break;              }              default:              {                  // All other error codes indicate a serious error has                  // occured.  Process this error.                  port->ProcessErrorMessage("WaitCommEvent()");                  break;              }              }          }          else          {              // If WaitCommEvent() returns TRUE, check to be sure there are              // actually bytes in the buffer to read.              //              // If you are reading more than one byte at a time from the buffer              // (which this program does not do) you will have the situation occur              // where the first byte to arrive will cause the WaitForMultipleObjects()              // function to stop waiting.  The WaitForMultipleObjects() function              // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state              // as it returns.              //              // If in the time between the reset of this event and the call to              // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again              // to the signeled state. When the call to ReadFile() occurs, it will              // read all of the bytes from the buffer, and the program will              // loop back around to WaitCommEvent().              //              // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,              // but there are no bytes available to read.  If you proceed and call              // ReadFile(), it will return immediatly due to the async port setup, but              // GetOverlappedResults() will not return until the next character arrives.              //              // It is not desirable for the GetOverlappedResults() function to be in              // this state.  The thread shutdown event (event 0) and the WriteFile()              // event (Event2) will not work if the thread is blocked by GetOverlappedResults().              //              // The solution to this is to check the buffer with a call to ClearCommError().              // This call will reset the event handle, and if there are no bytes to read              // we can loop back through WaitCommEvent() again, then proceed.              // If there are really bytes to read, do nothing and proceed.               bResult = ClearCommError(port->m_hComm, &dwError, &comstat);               if (comstat.cbInQue == 0)                  continue;          } // end if bResult           // Main wait function.  This function will normally block the thread          // until one of nine events occur that require action.          Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);           switch (Event)          {          case 0:          {              // Shutdown event.  This is event zero so it will be              // the higest priority and be serviced first.               port->m_bThreadAlive = FALSE;               // Kill this thread.  break is not needed, but makes me feel better.              AfxEndThread(100);              break;          }          case 1: // read event          {              GetCommMask(port->m_hComm, &CommEvent);              if (CommEvent & EV_CTS)                  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);              if (CommEvent & EV_RXFLAG)                  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);              if (CommEvent & EV_BREAK)                  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);              if (CommEvent & EV_ERR)                  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);              if (CommEvent & EV_RING)                  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);               if (CommEvent & EV_RXCHAR)                  // Receive character event from port.                  ReceiveChar(port, comstat);               break;          }          case 2: // write event          {              // Write character event from port              WriteChar(port);              break;          }           } // end switch       } // close forever loop       return 0;  }   //  // start comm watching  //  BOOL CSerialPort::StartMonitoring()  {      if (!(m_Thread = AfxBeginThread(CommThread, this)))          return FALSE;      TRACE("Thread started\n");      return TRUE;  }   //  // Restart the comm thread  //  BOOL CSerialPort::RestartMonitoring()  {      TRACE("Thread resumed\n");      m_Thread->ResumeThread();      return TRUE;  }    //  // Suspend the comm thread  //  BOOL CSerialPort::StopMonitoring()  {      TRACE("Thread suspended\n");      m_Thread->SuspendThread();      return TRUE;  }    //  // If there is a error, give the right message  //  void CSerialPort::ProcessErrorMessage(char *ErrorText)  {      char *Temp = new char[200];       LPVOID lpMsgBuf;       FormatMessage(          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,          NULL,          GetLastError(),          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language          (LPTSTR) &lpMsgBuf,          0,          NULL      );       sprintf(Temp, "WARNING:  %s Failed with the following error: \n%s\nPort: %d\n", (char *)ErrorText, lpMsgBuf, m_nPortNr);      MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);       LocalFree(lpMsgBuf);      delete[] Temp;  }   //  // Write a character.  //  void CSerialPort::WriteChar(CSerialPort *port)  {      BOOL bWrite = TRUE;      BOOL bResult = TRUE;       DWORD BytesSent = 0;       ResetEvent(port->m_hWriteEvent);       // Gain ownership of the critical section      EnterCriticalSection(&port->m_csCommunicationSync);       if (bWrite)      {          // Initailize variables          port->m_ov.Offset = 0;          port->m_ov.OffsetHigh = 0;           // Clear buffer          PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);           bResult = WriteFile(port->m_hComm, // Handle to COMM Port                              port->m_szWriteBuffer, // Pointer to message buffer in calling finction                              //原句 strlen((char*)port->m_szWriteBuffer), // Length of message to send                              port->m_nWriteSize,//更改后,Length of message to send                              &BytesSent, // Where to store the number of bytes sent                              &port->m_ov); // Overlapped structure           // deal with any error codes          if (!bResult)          {              DWORD dwError = GetLastError();              switch (dwError)              {              case ERROR_IO_PENDING:              {                  // continue to GetOverlappedResults()                  BytesSent = 0;                  bWrite = FALSE;                  break;              }              default:              {                  // all other error codes                  port->ProcessErrorMessage("WriteFile()");              }              }          }          else          {              LeaveCriticalSection(&port->m_csCommunicationSync);          }      } // end if(bWrite)       if (!bWrite)      {          bWrite = TRUE;           bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port                                        &port->m_ov, // Overlapped structure                                        &BytesSent, // Stores number of bytes sent                                        TRUE);  // Wait flag           LeaveCriticalSection(&port->m_csCommunicationSync);           // deal with the error code          if (!bResult)          {              port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");          }      } // end if (!bWrite)       // Verify that the data size send equals what we tried to send      //原句 if (BytesSent != strlen((char*)port->m_szWriteBuffer))      if(BytesSent |= port->m_nWriteSize) //修改后,Length of message to send      {          TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char *)port->m_szWriteBuffer));      }  }   //  // Character received. Inform the owner  //  void CSerialPort::ReceiveChar(CSerialPort *port, COMSTAT comstat)  {      BOOL  bRead = TRUE;      BOOL  bResult = TRUE;      DWORD dwError = 0;      DWORD BytesRead = 0;      unsigned char RXBuff;       for (;;)      {          // Gain ownership of the comm port critical section.          // This process guarantees no other part of this program          // is using the port object.            //添加信息 防止死锁          if(WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)              return ;            EnterCriticalSection(&port->m_csCommunicationSync);           // ClearCommError() will update the COMSTAT structure and          // clear any other errors.           bResult = ClearCommError(port->m_hComm, &dwError, &comstat);           LeaveCriticalSection(&port->m_csCommunicationSync);           // start forever loop.  I use this type of loop because I          // do not know at runtime how many loops this will have to          // run. My solution is to start a forever loop and to          // break out of it when I have processed all of the          // data available.  Be careful with this approach and          // be sure your loop will exit.          // My reasons for this are not as clear in this sample          // as it is in my production code, but I have found this          // solutiion to be the most efficient way to do this.           if (comstat.cbInQue == 0)          {              // break out when all bytes have been read              break;          }           EnterCriticalSection(&port->m_csCommunicationSync);           if (bRead)          {              bResult = ReadFile(port->m_hComm, // Handle to COMM port                                 &RXBuff, // RX Buffer Pointer                                 1, // Read one byte                                 &BytesRead, // Stores number of bytes read                                 &port->m_ov); // pointer to the m_ov structure              // deal with the error code              if (!bResult)              {                  switch (dwError = GetLastError())                  {                  case ERROR_IO_PENDING:                  {                      // asynchronous i/o is still in progress                      // Proceed on to GetOverlappedResults();                      bRead = FALSE;                      break;                  }                  default:                  {                      // Another error has occured.  Process this error.                      port->ProcessErrorMessage("ReadFile()");                      break;                  }                  }              }              else              {                  // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()                  bRead = TRUE;              }          }  // close if (bRead)           if (!bRead)          {              bRead = TRUE;              bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port                                            &port->m_ov, // Overlapped structure                                            &BytesRead, // Stores number of bytes read                                            TRUE);  // Wait flag               // deal with the error code              if (!bResult)              {                  port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");              }          }  // close if (!bRead)           LeaveCriticalSection(&port->m_csCommunicationSync);           // notify parent that a byte was received          ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);      } // end forever loop   }   //  // Write a string to the port  //  void CSerialPort::WriteToPort(char *string)  {      assert(m_hComm != 0);       memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));      strcpy(m_szWriteBuffer, string);      m_nWriteSize = strlen(string);       // set event for write      SetEvent(m_hWriteEvent);  }   //  // Return the device control block  //  DCB CSerialPort::GetDCB()  {      return m_dcb;  }   //  // Return the communication event masks  //  DWORD CSerialPort::GetCommEvents()  {      return m_dwCommEvents;  }   //  // Return the output buffer size  //  DWORD CSerialPort::GetWriteBufferSize()  {      return m_nWriteBufferSize;  }    void CSerialPort::WriteToPort(char *string, int n)  {      assert(m_hComm != 0);      memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));      memcpy(m_szWriteBuffer, string, n);      m_nWriteSize = n;      //set event for write      SetEvent(m_hWriteEvent);  }   void CSerialPort::WriteToPort(LPCTSTR string)  {      assert(m_hComm != 0);      memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));      strcpy(m_szWriteBuffer, string);      m_nWriteSize = strlen(string);      //set event for write      SetEvent(m_hWriteEvent);  }   void CSerialPort::WriteToPort(LPCTSTR string, int n)  {      assert(m_hComm != 0);      memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));      memcpy(m_szWriteBuffer, string, n);      m_nWriteSize = n;      //set event for write      SetEvent(m_hWriteEvent);  }   void CSerialPort::ClosePort()  {      if (m_bThreadAlive)      {          MSG message;          while (m_bThreadAlive)          {              if(::PeekMessage(&message, m_pOwner->m_hWnd, 0, 0, PM_REMOVE))              {                  ::TranslateMessage(&message);                  ::DispatchMessage(&message);              }              SetEvent(m_hShutdownEvent);          }          TRACE("Thread ended\n");       }      if(m_szWriteBuffer != NULL)      {          delete [] m_szWriteBuffer;          m_szWriteBuffer = NULL;      }       if(m_hComm)      {          CloseHandle(m_hComm);          m_hComm = NULL;     } } 

1 2
原创粉丝点击