CSerialPort类解析

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,  0 sizeof (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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值