串口serialport.cpp与serialport.h类文件源代码

serialport.cpp

#include "stdafx.h"
#include "SerialPort.h"

#include <assert.h>

int m_nComArray[20];//存放活跃的串口号
//
// 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 = 1;
    m_bIsSuspened = FALSE;
}

//
// Delete dynamic memory
//
CSerialPort::~CSerialPort()
{
    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    //串口句柄无效  add by itas109 2016-07-29
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
        return;
    }

    do
    {
        SetEvent(m_hShutdownEvent);
        //add by liquanhai  防止死锁  2011-11-06
        if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
        {
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
        }
    } while (m_bThreadAlive);

    // if the port is still opened: close it 
    if (m_hComm != NULL)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
    }
    // Close Handles  
    if (m_hShutdownEvent != NULL)
        CloseHandle(m_hShutdownEvent);
    if (m_ov.hEvent != NULL)
        CloseHandle(m_ov.hEvent);
    if (m_hWriteEvent != NULL)
        CloseHandle(m_hWriteEvent);

    //TRACE("Thread ended\n");

    if (m_szWriteBuffer != NULL)
    {
        delete[] m_szWriteBuffer;
        m_szWriteBuffer = NULL;
    }
}

//
// Initialize the port. This can be port 1 to MaxSerialPortNum.
///初始化串口。只能是1-MaxSerialPortNum
//
//parity:
//  n=none
//  e=even
//  o=odd
//  m=mark
//  s=space
//data:
//  5,6,7,8
//stop:
//  1,1.5,2 
//
BOOL CSerialPort::InitPort(HWND pPortOwner,    // the owner (CWnd) of the port (receives message)
    UINT  portnr,        // 串口号 (1..MaxSerialPortNum)
    UINT  baud,            // 速率
    TCHAR  parity,        // parity 
    UINT  databits,        // databits 
    UINT  stopbits,        // stopbits 
    DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc
    UINT  writebuffersize,// size to the writebuffer

    DWORD   ReadIntervalTimeout,
    DWORD   ReadTotalTimeoutMultiplier,
    DWORD   ReadTotalTimeoutConstant,
    DWORD   WriteTotalTimeoutMultiplier,
    DWORD   WriteTotalTimeoutConstant)

{
    assert(portnr > 0 && portnr < MaxSerialPortNum);
    assert(pPortOwner != NULL);

    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    // if the thread is alive: Kill
    if (m_bThreadAlive)
    {
        do
        {
            SetEvent(m_hShutdownEvent);
            //add by liquanhai  防止死锁  2011-11-06
            if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
            {
                ::TranslateMessage(&message);
                ::DispatchMessage(&message);
            }
        } while (m_bThreadAlive);
        //TRACE("Thread ended\n");
        //此处的延时很重要,因为如果串口开着,发送关闭指令到彻底关闭需要一定的时间,这个延时应该跟电脑的性能相关
        Sleep(50);//add by itas109 2016-08-02
    }

    // create events
    if (m_ov.hEvent != NULL)

SerialPort.h

#ifndef __SERIALPORT_H__
#define __SERIALPORT_H__

#include "stdio.h"
#include<windows.h>

struct serialPortInfo
{
    UINT portNr;//串口号
    DWORD bytesRead;//读取的字节数
};

#ifndef Wm_SerialPort_MSG_BASE 
#define Wm_SerialPort_MSG_BASE        WM_USER + 617        //!< 消息编号的基点  
#endif

#define Wm_SerialPort_BREAK_DETECTED    Wm_SerialPort_MSG_BASE + 1    // A break was detected on input.
#define Wm_SerialPort_CTS_DETECTED        Wm_SerialPort_MSG_BASE + 2    // The CTS (clear-to-send) signal changed state. 
#define Wm_SerialPort_DSR_DETECTED        Wm_SerialPort_MSG_BASE + 3    // The DSR (data-set-ready) signal changed state. 
#define Wm_SerialPort_ERR_DETECTED        Wm_SerialPort_MSG_BASE + 4    // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 
#define Wm_SerialPort_RING_DETECTED        Wm_SerialPort_MSG_BASE + 5    // A ring indicator was detected. 
#define Wm_SerialPort_RLSD_DETECTED        Wm_SerialPort_MSG_BASE + 6    // The RLSD (receive-line-signal-detect) signal changed state. 
#define Wm_SerialPort_RXCHAR            Wm_SerialPort_MSG_BASE + 7    // A character was received and placed in the input buffer. 
#define Wm_SerialPort_RXFLAG_DETECTED    Wm_SerialPort_MSG_BASE + 8    // The event character was received and placed in the input buffer.  
#define Wm_SerialPort_TXEMPTY_DETECTED    Wm_SerialPort_MSG_BASE + 9    // The last character in the output buffer was sent.  
#define Wm_SerialPort_RXSTR             Wm_SerialPort_MSG_BASE + 10   // Receive string

#define MaxSerialPortNum 200   ///有效的串口总个数,不是串口的号 //add by itas109 2014-01-09
#define IsReceiveString  0     //采用何种方式接收:ReceiveString 1多字符串接收(对应响应函数为Wm_SerialPort_RXSTR),ReceiveString 0一个字符一个字符接收(对应响应函数为Wm_SerialPort_RXCHAR)
class CSerialPort
{
public:
    // contruction and destruction
    CSerialPort();
    virtual        ~CSerialPort();

    // port initialisation        
    // UINT stopsbits = ONESTOPBIT   stop is index 0 = 1 1=1.5 2=2 
    // 切记:stopsbits = 1,不是停止位为1。
    // by itas109 20160506
    BOOL        InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 9600,
        TCHAR parity = _T('N'), UINT databits = 8, UINT stopsbits = ONESTOPBIT,
        DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,

        DWORD ReadIntervalTimeout = 1000,
        DWORD ReadTotalTimeoutMultiplier = 1000,
        DWORD ReadTotalTimeoutConstant = 1000,
        DWORD WriteTotalTimeoutMultiplier = 1000,
        DWORD WriteTotalTimeoutConstant = 1000);

    // start/stop comm watching
    ///控制串口监视线程
    BOOL         StartMonitoring();//开始监听
    BOOL         ResumeMonitoring();//恢复监听
    BOOL         SuspendMonitoring();//挂起监听
    BOOL         IsThreadSuspend(HANDLE hThread);//判断线程是否挂起 //add by itas109 2016-06-29

    DWORD         GetWriteBufferSize();///获取写缓冲大小
    DWORD         GetCommEvents();///获取事件
    DCB             GetDCB();///获取DCB

    ///写数据到串口
    void        WriteToPort(char* string, size_t n); // add by mrlong 2007-12-25
    void        WriteToPort(PBYTE Buffer, size_t n);// add by mrlong
    void        ClosePort();                     // add by mrlong 2007-12-2  
    BOOL        IsOpen();

    void QueryKey(HKEY hKey);///查询注册表的串口号,将值存于数组中

#ifdef _AFX
    void Hkey2ComboBox(CComboBox& m_PortNO);///将QueryKey查询到的串口号添加到CComboBox控件中
#endif // _AFX

protected:
    // protected memberfunctions
    void        ProcessErrorMessage(TCHAR* ErrorText);///错误处理
    static DWORD WINAPI CommThread(LPVOID pParam);///线程函数
    static void    ReceiveChar(CSerialPort* port);
    static void ReceiveStr(CSerialPort* port); //add by itas109 2016-06-22
    static void    WriteChar(CSerialPort* port);

    // thread
    HANDLE                m_Thread;
    BOOL                m_bIsSuspened;///thread监视线程是否挂起

    // synchronisation objects
    CRITICAL_SECTION    m_csCommunicationSync;///临界资源
    BOOL                m_bThreadAlive;///监视线程运行标志

    // handles
    HANDLE                m_hShutdownEvent;  //stop发生的事件
    HANDLE                m_hComm;           // 串口句柄 
    HANDLE                m_hWriteEvent;     // write

OVERLAPPED            m_ov;///异步I/O
    COMMTIMEOUTS        m_SerialPortTimeouts;///超时设置
    DCB                    m_dcb;///设备控制块

    // owner window
    HWND                m_pOwner;

    // misc
    UINT                m_nPortNr;        ///串口号
    PBYTE                m_szWriteBuffer;///写缓冲区
    DWORD                m_dwCommEvents;
    DWORD                m_nWriteBufferSize;///写缓冲大小

    size_t                m_nWriteSize;//写入字节数 //add by mrlong 2007-12-25
};

#endif __SERIALPORT_H__
 

        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
    //为避免有些串口设备无数据输入,但一直返回读事件,使监听线程阻塞,
    //可以将读写放在两个线程中,或者修改读写事件优先级
    //修改优先级有两个方案:
    //方案一为监听线程中WaitCommEvent()后,添加如下两条语句:
    //if (WAIT_OBJECT_O == WaitForSingleObject(port->m_hWriteEvent, 0))
    //    ResetEvent(port->m_ov.hEvent);
    //方案二为初始化时即修改,即下面两条语句:
    m_hEventArray[1] = m_hWriteEvent;
    m_hEventArray[2] = m_ov.hEvent;


    // 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 = NULL;
    }
    m_szWriteBuffer = new BYTE[writebuffersize];

    m_nPortNr = portnr;

    m_nWriteBufferSize = writebuffersize;
    m_dwCommEvents = dwCommEvents;

    BOOL bResult = FALSE;
    TCHAR *szPort = new TCHAR[MAX_PATH];
    TCHAR *szBaud = new TCHAR[MAX_PATH];

    /*
    多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,
    无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每
    个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和
    LeaveCriticalSection函数。
    */
    // 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
    _stprintf_s(szPort, MAX_PATH, _T("\\\\.\\COM%d"), portnr);///可以显示COM10以上端口//add by itas109 2014-01-09

    // stop is index 0 = 1 1=1.5 2=2
    int mystop;
    int myparity;
    switch (stopbits)
    {
    case 0:
        mystop = ONESTOPBIT;
        break;
    case 1:
        mystop = ONE5STOPBITS;
        break;
    case 2:
        mystop = TWOSTOPBITS;
        break;
        //增加默认情况,因为stopbits=1.5时,SetCommState会报错。
        //一般的电脑串口不支持1.5停止位,这个1.5停止位似乎用在红外传输上的。
        //by itas109 20160506
    default:
        mystop = ONESTOPBIT;
        break;
    }
    myparity = 0;
    parity = _totupper(parity);
    switch (parity)
    {
    case _T('N'):
        myparity = 0;
        break;
    case _T('O'):
        myparity = 1;
        break;
    case _T('E'):
        myparity = 2;
        break;
    case _T('M'):
        myparity = 3;
        break;
    case _T('S'):
        myparity = 4;
        break;
        //增加默认情况。
        //by itas109 20160506
    default:
        myparity = 0;
        break;
    }
    _stprintf_s(szBaud, MAX_PATH, _T("baud=%d parity=%c data=%d stop=%d"), baud, parity, databits, mystop);

    // get a handle to the port
    /*
    通信程序在CreateFile处指定串口设备及相关的操作属性,再返回一个句柄,
    该句柄将被用于后续的通信操作,并贯穿整个通信过程串口打开后,其属性
    被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&&dcb)读取
    当前串口设备控制块DCB设置,修改后通过SetCommState(hComm,&&dcb)将其写
    入。运用ReadFile()与WriteFile()这两个API函数实现串口读写操作,若为异
    步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读
    写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING,
    表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject()
    来等待结束信号并设置最长等待时间
    */
    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)
    {
        //add by itas109 2016-08-02
        //串口打开失败,增加提示信息
        switch (GetLastError())
        {
            //串口不存在
        case ERROR_FILE_NOT_FOUND:
        {
                                     TCHAR Temp[200] = { 0 };
                                     _stprintf_s(Temp, 200, _T("COM%d ERROR_FILE_NOT_FOUND,Error Code:%d"), portnr, GetLastError());
                                     MessageBox(NULL, Temp, _T("COM InitPort Error"), MB_ICONERROR);
                                     break;
        }
            //串口拒绝访问
        case ERROR_ACCESS_DENIED:
        {
                                    TCHAR Temp[200] = { 0 };
                                    _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,Error Code:%d"), portnr, GetLastError());
                                    MessageBox(NULL, Temp, _T("COM InitPort Error"), MB_ICONERROR);
                                    break;
        }
        default:
            break;
        }
        // port not found
        delete[] szPort;
        delete[] szBaud;

        return FALSE;
    }

    // set the timeout values
    ///设置超时
    m_SerialPortTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
    m_SerialPortTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
    m_SerialPortTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
    m_SerialPortTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
    m_SerialPortTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000;

    // configure
    ///配置
    ///分别调用Windows API设置串口参数
    if (SetCommTimeouts(m_hComm, &m_SerialPortTimeouts))///设置超时
    {
        /*
        若对端口数据的响应时间要求较严格,可采用事件驱动方式。
        事件驱动方式通过设置事件通知,当所希望的事件发生时,Windows
        发出该事件已发生的通知,这与DOS环境下的中断方式很相似。Windows
        定义了9种串口通信事件,较常用的有以下三种:
        EV_RXCHAR:接收到一个字节,并放入输入缓冲区;
        EV_TXEMPTY:输出缓冲区中的最后一个字符,发送出去;
        EV_RXFLAG:接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区
        在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事
        件的发生。SetCommMask(hComm,0)可使WaitCommEvent()中止
        */
        if (SetCommMask(m_hComm, dwCommEvents))///设置通信事件
        {

            if (GetCommState(m_hComm, &m_dcb))///获取当前DCB参数
            {
                m_dcb.EvtChar = 'q';
                m_dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS bit high!
                m_dcb.BaudRate = baud;  // add by mrlong
                m_dcb.Parity = myparity;
                m_dcb.ByteSize = databits;
                m_dcb.StopBits = mystop;

                //if (BuildCommDCB(szBaud &m_dcb))///填写DCB结构
                //{
                if (SetCommState(m_hComm, &m_dcb))///配置DCB
                    ; // normal operation... continue
                else
                    ProcessErrorMessage(_T("SetCommState()"));
                //}
                //else
                //    ProcessErrorMessage("BuildCommDCB()");
            }
            else
                ProcessErrorMessage(_T("GetCommState()"));
        }
        else
            ProcessErrorMessage(_T("SetCommMask()"));
    }
    else
        ProcessErrorMessage(_T("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.
///线程函数
///监视线程的大致流程:
///检查串口-->进入循环{WaitCommEvent(不阻塞询问)询问事件-->如果有事件来到-->到相应处理(关闭\读\写)}
//
DWORD WINAPI 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.
    ///TRUE表示线程正在运行
    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!

        /*
        WaitCommEvent函数第3个参数1pOverlapped可以是一个OVERLAPPED结构的变量指针
        ,也可以是NULL,当用NULL时,表示该函数是同步的,否则表示该函数是异步的。
        调用WaitCommEvent时,如果异步操作不能立即完成,会立即返回FALSE,系统在
        WaitCommEvent返回前将OVERLAPPED结构成员hEvent设为无信号状态,等到产生通信
        事件时,系统将其置有信号
        */

        bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);///表示该函数是异步的

        if (!bResult)
        {
            // If WaitCommEvent() returns FALSE, process the last error to determin
            // the reason..
            ///如果WaitCommEvent返回Error为FALSE,则查询错误信息
            switch (dwError = GetLastError())
            {
            case ERROR_IO_PENDING:     ///正常情况,没有字符可读 erroe code:997
            {
                                        // This is a normal return value if there are no bytes
                                        // to read at the port.
                                        // Do nothing and continue
                                        break;
            }
            case ERROR_INVALID_PARAMETER:///系统错误 erroe code: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;
            }
            case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
            {
                                         port->m_hComm = INVALID_HANDLE_VALUE;
                                         TCHAR Temp[200] = { 0 };
                                         _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,WaitCommEvent() Error Code:%d"), port->m_nPortNr, GetLastError());
                                         MessageBox(NULL, Temp, _T("COM WaitCommEvent Error"), MB_ICONERROR);
                                         break;
            }
            case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
            {
                                          port->m_hComm = INVALID_HANDLE_VALUE;
                                          break;
            }
            case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
            {
                                       port->m_hComm = INVALID_HANDLE_VALUE;
                                       TCHAR Temp[200] = { 0 };
                                       _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,WaitCommEvent() Error Code:%d"), port->m_nPortNr, GetLastError());
                                       MessageBox(NULL, Temp, _T("COM WaitCommEvent Error"), MB_ICONERROR);
                                       break;
            }
            default:///发生其他错误,其中有串口读写中断开串口连接的错误(错误22)
            {
                        // All other error codes indicate a serious error has
                        //发生错误时,将串口句柄置为无效句柄
                        port->m_hComm = INVALID_HANDLE_VALUE;
                        // occured.  Process this error.
                        port->ProcessErrorMessage(_T("WaitCommEvent()"));
                        break;
            }
            }
        }
        else    ///WaitCommEvent()能正确返回
        {
            // 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.
        ///等待3个事件:关断/读/写,有一个事件发生就返回
        Event = WaitForMultipleObjects(3, ///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.
                  ///关断事件,关闭串口
                  CloseHandle(port->m_hComm);
                  port->m_hComm = NULL;
                  port->m_bThreadAlive = FALSE;

                  // Kill this thread.  break is not needed, but makes me feel better.
                  //AfxEndThread(100);
                  ::ExitThread(100);

                  break;
        }
        case 1: // write event 发送数据
        {
                    // Write character event from port
                    WriteChar(port);
                    break;
        }
        case 2:    // read event 将定义的各种消息发送出去
        {
                    GetCommMask(port->m_hComm, &CommEvent);
                    if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
                    {
                        if (IsReceiveString == 1)
                        {
                            ReceiveStr(port);//多字符接收
                        }
                        else if (IsReceiveString == 0)
                        {
                            ReceiveChar(port);//单字符接收
                        }
                        else
                        {
                            //默认多字符接收
                            ReceiveStr(port);//多字符接收
                        }
                    }

                    if (CommEvent & EV_CTS) //CTS信号状态发生变化
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_CTS_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中 
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_RXFLAG_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_BREAK)  //输入中发生中断
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_BREAK_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY 
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_ERR_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_RING) //检测到振铃指示
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_RING_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);

                    break;
        }
        default:
        {
                   MessageBox(NULL, _T("Receive Error!"), _T("COM Receive Error"), MB_ICONERROR);
                   break;
        }

        } // end switch

    } // close forever loop

    return 0;
}

//
// start comm watching
///开启监视线程
//
BOOL CSerialPort::StartMonitoring()
{
    //if (!(m_Thread = AfxBeginThread(CommThread, this)))
    if (!(m_Thread = ::CreateThread(NULL, 0, CommThread, this, 0, NULL)))
        return FALSE;
    //TRACE("Thread started\n");
    return TRUE;
}

//
// Restart the comm thread
///从挂起恢复监视线程
//
BOOL CSerialPort::ResumeMonitoring()
{
    //TRACE("Thread resumed\n");
    //m_Thread->ResumeThread();
    ::ResumeThread(m_Thread);
    return TRUE;
}

//
// Suspend the comm thread
///挂起监视线程
//
BOOL CSerialPort::SuspendMonitoring()
{
    //TRACE("Thread suspended\n");
    //m_Thread->SuspendThread();
    ::SuspendThread(m_Thread);
    return TRUE;
}

BOOL CSerialPort::IsThreadSuspend(HANDLE hThread)
{
    DWORD   count = SuspendThread(hThread);
    if (count == -1)
    {
        return FALSE;
    }
    ResumeThread(hThread);
    return (count != 0);
}

//
// If there is a error, give the right message
///如果有错误,给出提示
//
void CSerialPort::ProcessErrorMessage(TCHAR* ErrorText)
{
    TCHAR Temp[200] = { 0 };

    LPVOID lpMsgBuf;

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR)&lpMsgBuf,
        0,
        NULL
        );

    _stprintf_s(Temp, 200, _T("WARNING:  %s Failed with the following error: \n%s\nPort: %d\n"), ErrorText, (TCHAR*)lpMsgBuf, m_nPortNr);
    MessageBox(NULL, Temp, _T("Application Error"), MB_ICONSTOP);

    LocalFree(lpMsgBuf);
}

//
// Write a character.
//
void CSerialPort::WriteChar(CSerialPort* port)
{
    BOOL bWrite = TRUE;
    BOOL bResult = TRUE;

    DWORD BytesSent = 0;
    DWORD SendLen = port->m_nWriteSize;
    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
            SendLen,    // add by mrlong
            //strlen((char*)port->m_szWriteBuffer),    // 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;
            }
            case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
            {
                                         port->m_hComm = INVALID_HANDLE_VALUE;
                                         TCHAR Temp[200] = { 0 };
                                         _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,WriteFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                         MessageBox(NULL, Temp, _T("COM WriteFile Error"), MB_ICONERROR);
                                         break;
            }
            case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
            {
                                          port->m_hComm = INVALID_HANDLE_VALUE;
                                          break;
            }
            case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
            {
                                       port->m_hComm = INVALID_HANDLE_VALUE;
                                       TCHAR Temp[200] = { 0 };
                                       _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,WriteFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                       MessageBox(NULL, Temp, _T("COM WriteFile Error"), MB_ICONERROR);
                                       break;
            }
            default:
            {
                       // all other error codes
                       port->ProcessErrorMessage(_T("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(_T("GetOverlappedResults() in WriteFile()"));
        }
    } // end if (!bWrite)

    // Verify that the data size send equals what we tried to send
    //if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by 
    //{
    //TRACE(_T("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n"), BytesSent, _tcsclen((TCHAR*)port->m_szWriteBuffer));
    //}
}

//
// Character received. Inform the owner
//
void CSerialPort::ReceiveChar(CSerialPort* port)
{
    BOOL  bRead = TRUE;
    BOOL  bResult = TRUE;
    DWORD dwError = 0;
    DWORD BytesRead = 0;
    COMSTAT comstat;
    unsigned char RXBuff;

    for (;;)
    {
        //add by liquanhai 2011-11-06  防止死锁
        if (WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)
            return;

        // Gain ownership of the comm port critical section.
        // This process guarantees no other part of this program 
        // is using the port object. 

        EnterCriticalSection(&port->m_csCommunicationSync);

        // ClearCommError() will update the COMSTAT structure and
        // clear any other errors.
        ///更新COMSTAT

        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();
                                         ///异步IO仍在进行
                                         bRead = FALSE;
                                         break;
                }
                case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
                {
                                             port->m_hComm = INVALID_HANDLE_VALUE;
                                             TCHAR Temp[200] = { 0 };
                                             _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                             MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                             break;
                }
                case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
                {
                                              port->m_hComm = INVALID_HANDLE_VALUE;
                                              break;
                }
                case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
                {
                                           port->m_hComm = INVALID_HANDLE_VALUE;
                                           TCHAR Temp[200] = { 0 };
                                           _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                           MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                           break;
                }
                default:
                {
                           // Another error has occured.  Process this error.
                           port->ProcessErrorMessage(_T("ReadFile()"));
                           break;
                           //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多
                }
                }
            }
            else///ReadFile返回TRUE
            {
                // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
                bRead = TRUE;
            }
        }  // close if (bRead)

        ///异步IO操作仍在进行,需要调用GetOverlappedResult查询
        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(_T("GetOverlappedResults() in ReadFile()"));
            }
        }  // close if (!bRead)

        LeaveCriticalSection(&port->m_csCommunicationSync);

        // notify parent that a byte was received
        //避免线程互相等待,产生死锁,使用PostMessage()代替SendMessage()
        PostMessage(port->m_pOwner, Wm_SerialPort_RXCHAR, (WPARAM)RXBuff, (LPARAM)port->m_nPortNr);
        //::SendMessage((port->m_pOwner), Wm_SerialPort_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
    } // end forever loop

}

//
// str received. Inform the owner
//
void CSerialPort::ReceiveStr(CSerialPort* port)
{
    BOOL  bRead = TRUE;
    BOOL  bResult = TRUE;
    DWORD dwError = 0;
    DWORD BytesRead = 0;
    COMSTAT comstat;
    serialPortInfo commInfo;

    for (;;)
    {
        //add by liquanhai 2011-11-06  防止死锁
        if (WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)
            return;

        // Gain ownership of the comm port critical section.
        // This process guarantees no other part of this program 
        // is using the port object. 

        EnterCriticalSection(&port->m_csCommunicationSync);

        // ClearCommError() will update the COMSTAT structure and
        // clear any other errors.
        ///更新COMSTAT

        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.

        ///所有字符均被读出,中断循环
        //0xcccccccc表示串口异常了,会导致RXBuff指针初始化错误
        if (comstat.cbInQue == 0 || comstat.cbInQue == 0xcccccccc)
        {
            // break out when all bytes have been read
            break;
        }

        //如果遇到'\0',那么数据会被截断,实际数据全部读取只是没有显示完全,这个时候使用memcpy才能全部获取
        unsigned char* RXBuff = new unsigned char[comstat.cbInQue + 1];
        if (RXBuff == NULL)
        {
            return;
        }
        RXBuff[comstat.cbInQue] = '\0';//附加字符串结束符

        EnterCriticalSection(&port->m_csCommunicationSync);

        if (bRead)
        {
            ///串口读出,读出缓冲区中字节
            bResult = ReadFile(port->m_hComm,        // Handle to COMM port 
                RXBuff,                // RX Buffer Pointer
                comstat.cbInQue,                    // Read cbInQue len 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();
                                         ///异步IO仍在进行
                                         bRead = FALSE;
                                         break;
                }
                case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
                {
                                             port->m_hComm = INVALID_HANDLE_VALUE;
                                             TCHAR Temp[200] = { 0 };
                                             _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                             MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                             break;
                }
                case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
                {
                                              port->m_hComm = INVALID_HANDLE_VALUE;
                                              break;
                }
                case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
                {
                                           port->m_hComm = INVALID_HANDLE_VALUE;
                                           TCHAR Temp[200] = { 0 };
                                           _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                           MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                           break;
                }
                default:
                {
                           // Another error has occured.  Process this error.
                           port->ProcessErrorMessage(_T("ReadFile()"));
                           break;
                           //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多
                }
                }
            }
            else///ReadFile返回TRUE
            {
                // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
                bRead = TRUE;
            }
        }  // close if (bRead)

        ///异步IO操作仍在进行,需要调用GetOverlappedResult查询
        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(_T("GetOverlappedResults() in ReadFile()"));
            }
        }  // close if (!bRead)

        LeaveCriticalSection(&port->m_csCommunicationSync);

        commInfo.portNr = port->m_nPortNr;
        commInfo.bytesRead = BytesRead;
        // notify parent that some byte was received
        ::SendMessage((port->m_pOwner), Wm_SerialPort_RXSTR, (WPARAM)RXBuff, (LPARAM)&commInfo);

        //释放
        delete[] RXBuff;
        RXBuff = NULL;

    } // end forever loop

}

//
// 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;
}

BOOL CSerialPort::IsOpen()
{
    return m_hComm != NULL && m_hComm != INVALID_HANDLE_VALUE;//m_hComm增加INVALID_HANDLE_VALUE的情况 add by itas109 2016-07-29
}

void CSerialPort::ClosePort()
{
    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    //串口句柄无效  add by itas109 2016-07-29
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
        return;
    }

    do
    {
        SetEvent(m_hShutdownEvent);
        //add by liquanhai  防止死锁  2011-11-06
        if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
        {
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
        }
    } while (m_bThreadAlive);

    // if the port is still opened: close it 
    if (m_hComm != NULL)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
    }

    // Close Handles  
    if (m_hShutdownEvent != NULL)
    {
        ResetEvent(m_hShutdownEvent);
    }
    if (m_ov.hEvent != NULL)
    {
        ResetEvent(m_ov.hEvent);
    }
    if (m_hWriteEvent != NULL)
    {
        ResetEvent(m_hWriteEvent);
        //CloseHandle(m_hWriteEvent);//开发者反映,这里会导致多个串口工作时,重新打开串口异常
    }

    if (m_szWriteBuffer != NULL)
    {
        delete[] m_szWriteBuffer;
        m_szWriteBuffer = NULL;
    }
}


void CSerialPort::WriteToPort(char* string, size_t 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(BYTE* Buffer, size_t n)
{
    assert(m_hComm != 0);
    memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
    memcpy(m_szWriteBuffer, Buffer, n);
    /*
    int i;
    for(i=0; i<n; i++)
    {
    m_szWriteBuffer[i] = Buffer[i];
    }
    */
    m_nWriteSize = n;

    // set event for write
    SetEvent(m_hWriteEvent);
}

///查询注册表的串口号,将值存于数组中
///本代码参考于mingojiang的获取串口逻辑名代码
//
void CSerialPort::QueryKey(HKEY hKey)
{
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
    //    TCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
    //    DWORD    cbName;                   // size of name string 
    TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name 
    DWORD    cchClassName = MAX_PATH;  // size of class string 
    DWORD    cSubKeys = 0;               // number of subkeys 
    DWORD    cbMaxSubKey;              // longest subkey size 
    DWORD    cchMaxClass;              // longest class string 
    DWORD    cValues;              // number of values for key 
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data 
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 

    DWORD i, retCode;

    TCHAR  achValue[MAX_VALUE_NAME];
    DWORD cchValue = MAX_VALUE_NAME;

    // Get the class name and the value count. 
    retCode = RegQueryInfoKey(
        hKey,                    // key handle 
        achClass,                // buffer for class name 
        &cchClassName,           // size of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 

    for (i = 0; i<20; i++)///存放串口号的数组初始化
    {
        m_nComArray[i] = -1;
    }

    // Enumerate the key values. 
    if (cValues > 0) {
        for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++)
        {
            cchValue = MAX_VALUE_NAME;
            achValue[0] = '\0';
            if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL, NULL, NULL))
            {
                TCHAR strDSName[10];
                memset(strDSName, 0, 10);
                DWORD nValueType = 0, nBuffLen = 10;
                if (ERROR_SUCCESS == RegQueryValueEx(hKey, (LPCTSTR)achValue, NULL, &nValueType, (LPBYTE)strDSName, &nBuffLen))
                {
                    int nIndex = -1;
                    while (++nIndex < MaxSerialPortNum)
                    {
                        if (-1 == m_nComArray[nIndex])
                        {
                            m_nComArray[nIndex] = _tstoi((TCHAR*)(strDSName + 3));
                            break;
                        }
                    }
                }
            }
        }
    }
    else{
        MessageBox(NULL, _T("No Com In This Computer!"), _T("COM Query Error"), MB_ICONERROR);
    }

}

#ifdef _AFX
void CSerialPort::Hkey2ComboBox(CComboBox& m_PortNO)
{
    HKEY hTestKey;
    bool Flag = FALSE;

    ///仅是XP系统的注册表位置,其他系统根据实际情况做修改
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_READ, &hTestKey))
    {
        QueryKey(hTestKey);
    }
    RegCloseKey(hTestKey);

    int i = 0;
    m_PortNO.ResetContent();///刷新时,清空下拉列表内容
    while (i < MaxSerialPortNum && -1 != m_nComArray[i])
    {
        CString szCom;
        szCom.Format(_T("COM%d"), m_nComArray[i]);
        m_PortNO.InsertString(i, szCom.GetBuffer(5));
        ++i;
        Flag = TRUE;
        if (Flag)///把第一个发现的串口设为下拉列表的默认值
            m_PortNO.SetCurSel(0);
    }
}
#endif // _AFX

serialport.cpp

#include "stdafx.h"
#include "SerialPort.h"

#include <assert.h>

int m_nComArray[20];//存放活跃的串口号
//
// 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 = 1;
    m_bIsSuspened = FALSE;
}

//
// Delete dynamic memory
//
CSerialPort::~CSerialPort()
{
    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    //串口句柄无效  add by itas109 2016-07-29
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
        return;
    }

    do
    {
        SetEvent(m_hShutdownEvent);
        //add by liquanhai  防止死锁  2011-11-06
        if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
        {
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
        }
    } while (m_bThreadAlive);

    // if the port is still opened: close it 
    if (m_hComm != NULL)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
    }
    // Close Handles  
    if (m_hShutdownEvent != NULL)
        CloseHandle(m_hShutdownEvent);
    if (m_ov.hEvent != NULL)
        CloseHandle(m_ov.hEvent);
    if (m_hWriteEvent != NULL)
        CloseHandle(m_hWriteEvent);

    //TRACE("Thread ended\n");

    if (m_szWriteBuffer != NULL)
    {
        delete[] m_szWriteBuffer;
        m_szWriteBuffer = NULL;
    }
}

//
// Initialize the port. This can be port 1 to MaxSerialPortNum.
///初始化串口。只能是1-MaxSerialPortNum
//
//parity:
//  n=none
//  e=even
//  o=odd
//  m=mark
//  s=space
//data:
//  5,6,7,8
//stop:
//  1,1.5,2 
//
BOOL CSerialPort::InitPort(HWND pPortOwner,    // the owner (CWnd) of the port (receives message)
    UINT  portnr,        // 串口号 (1..MaxSerialPortNum)
    UINT  baud,            // 速率
    TCHAR  parity,        // parity 
    UINT  databits,        // databits 
    UINT  stopbits,        // stopbits 
    DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc
    UINT  writebuffersize,// size to the writebuffer

    DWORD   ReadIntervalTimeout,
    DWORD   ReadTotalTimeoutMultiplier,
    DWORD   ReadTotalTimeoutConstant,
    DWORD   WriteTotalTimeoutMultiplier,
    DWORD   WriteTotalTimeoutConstant)

{
    assert(portnr > 0 && portnr < MaxSerialPortNum);
    assert(pPortOwner != NULL);

    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    // if the thread is alive: Kill
    if (m_bThreadAlive)
    {
        do
        {
            SetEvent(m_hShutdownEvent);
            //add by liquanhai  防止死锁  2011-11-06
            if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
            {
                ::TranslateMessage(&message);
                ::DispatchMessage(&message);
            }
        } while (m_bThreadAlive);
        //TRACE("Thread ended\n");
        //此处的延时很重要,因为如果串口开着,发送关闭指令到彻底关闭需要一定的时间,这个延时应该跟电脑的性能相关
        Sleep(50);//add by itas109 2016-08-02
    }

    // create events
    if (m_ov.hEvent != NULL)

SerialPort.h

#ifndef __SERIALPORT_H__
#define __SERIALPORT_H__

#include "stdio.h"
#include<windows.h>

struct serialPortInfo
{
    UINT portNr;//串口号
    DWORD bytesRead;//读取的字节数
};

#ifndef Wm_SerialPort_MSG_BASE 
#define Wm_SerialPort_MSG_BASE        WM_USER + 617        //!< 消息编号的基点  
#endif

#define Wm_SerialPort_BREAK_DETECTED    Wm_SerialPort_MSG_BASE + 1    // A break was detected on input.
#define Wm_SerialPort_CTS_DETECTED        Wm_SerialPort_MSG_BASE + 2    // The CTS (clear-to-send) signal changed state. 
#define Wm_SerialPort_DSR_DETECTED        Wm_SerialPort_MSG_BASE + 3    // The DSR (data-set-ready) signal changed state. 
#define Wm_SerialPort_ERR_DETECTED        Wm_SerialPort_MSG_BASE + 4    // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 
#define Wm_SerialPort_RING_DETECTED        Wm_SerialPort_MSG_BASE + 5    // A ring indicator was detected. 
#define Wm_SerialPort_RLSD_DETECTED        Wm_SerialPort_MSG_BASE + 6    // The RLSD (receive-line-signal-detect) signal changed state. 
#define Wm_SerialPort_RXCHAR            Wm_SerialPort_MSG_BASE + 7    // A character was received and placed in the input buffer. 
#define Wm_SerialPort_RXFLAG_DETECTED    Wm_SerialPort_MSG_BASE + 8    // The event character was received and placed in the input buffer.  
#define Wm_SerialPort_TXEMPTY_DETECTED    Wm_SerialPort_MSG_BASE + 9    // The last character in the output buffer was sent.  
#define Wm_SerialPort_RXSTR             Wm_SerialPort_MSG_BASE + 10   // Receive string

#define MaxSerialPortNum 200   ///有效的串口总个数,不是串口的号 //add by itas109 2014-01-09
#define IsReceiveString  0     //采用何种方式接收:ReceiveString 1多字符串接收(对应响应函数为Wm_SerialPort_RXSTR),ReceiveString 0一个字符一个字符接收(对应响应函数为Wm_SerialPort_RXCHAR)
class CSerialPort
{
public:
    // contruction and destruction
    CSerialPort();
    virtual        ~CSerialPort();

    // port initialisation        
    // UINT stopsbits = ONESTOPBIT   stop is index 0 = 1 1=1.5 2=2 
    // 切记:stopsbits = 1,不是停止位为1。
    // by itas109 20160506
    BOOL        InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 9600,
        TCHAR parity = _T('N'), UINT databits = 8, UINT stopsbits = ONESTOPBIT,
        DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,

        DWORD ReadIntervalTimeout = 1000,
        DWORD ReadTotalTimeoutMultiplier = 1000,
        DWORD ReadTotalTimeoutConstant = 1000,
        DWORD WriteTotalTimeoutMultiplier = 1000,
        DWORD WriteTotalTimeoutConstant = 1000);

    // start/stop comm watching
    ///控制串口监视线程
    BOOL         StartMonitoring();//开始监听
    BOOL         ResumeMonitoring();//恢复监听
    BOOL         SuspendMonitoring();//挂起监听
    BOOL         IsThreadSuspend(HANDLE hThread);//判断线程是否挂起 //add by itas109 2016-06-29

    DWORD         GetWriteBufferSize();///获取写缓冲大小
    DWORD         GetCommEvents();///获取事件
    DCB             GetDCB();///获取DCB

    ///写数据到串口
    void        WriteToPort(char* string, size_t n); // add by mrlong 2007-12-25
    void        WriteToPort(PBYTE Buffer, size_t n);// add by mrlong
    void        ClosePort();                     // add by mrlong 2007-12-2  
    BOOL        IsOpen();

    void QueryKey(HKEY hKey);///查询注册表的串口号,将值存于数组中

#ifdef _AFX
    void Hkey2ComboBox(CComboBox& m_PortNO);///将QueryKey查询到的串口号添加到CComboBox控件中
#endif // _AFX

protected:
    // protected memberfunctions
    void        ProcessErrorMessage(TCHAR* ErrorText);///错误处理
    static DWORD WINAPI CommThread(LPVOID pParam);///线程函数
    static void    ReceiveChar(CSerialPort* port);
    static void ReceiveStr(CSerialPort* port); //add by itas109 2016-06-22
    static void    WriteChar(CSerialPort* port);

    // thread
    HANDLE                m_Thread;
    BOOL                m_bIsSuspened;///thread监视线程是否挂起

    // synchronisation objects
    CRITICAL_SECTION    m_csCommunicationSync;///临界资源
    BOOL                m_bThreadAlive;///监视线程运行标志

    // handles
    HANDLE                m_hShutdownEvent;  //stop发生的事件
    HANDLE                m_hComm;           // 串口句柄 
    HANDLE                m_hWriteEvent;     // write

OVERLAPPED            m_ov;///异步I/O
    COMMTIMEOUTS        m_SerialPortTimeouts;///超时设置
    DCB                    m_dcb;///设备控制块

    // owner window
    HWND                m_pOwner;

    // misc
    UINT                m_nPortNr;        ///串口号
    PBYTE                m_szWriteBuffer;///写缓冲区
    DWORD                m_dwCommEvents;
    DWORD                m_nWriteBufferSize;///写缓冲大小

    size_t                m_nWriteSize;//写入字节数 //add by mrlong 2007-12-25
};

#endif __SERIALPORT_H__
 

        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
    //为避免有些串口设备无数据输入,但一直返回读事件,使监听线程阻塞,
    //可以将读写放在两个线程中,或者修改读写事件优先级
    //修改优先级有两个方案:
    //方案一为监听线程中WaitCommEvent()后,添加如下两条语句:
    //if (WAIT_OBJECT_O == WaitForSingleObject(port->m_hWriteEvent, 0))
    //    ResetEvent(port->m_ov.hEvent);
    //方案二为初始化时即修改,即下面两条语句:
    m_hEventArray[1] = m_hWriteEvent;
    m_hEventArray[2] = m_ov.hEvent;


    // 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 = NULL;
    }
    m_szWriteBuffer = new BYTE[writebuffersize];

    m_nPortNr = portnr;

    m_nWriteBufferSize = writebuffersize;
    m_dwCommEvents = dwCommEvents;

    BOOL bResult = FALSE;
    TCHAR *szPort = new TCHAR[MAX_PATH];
    TCHAR *szBaud = new TCHAR[MAX_PATH];

    /*
    多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,
    无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每
    个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和
    LeaveCriticalSection函数。
    */
    // 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
    _stprintf_s(szPort, MAX_PATH, _T("\\\\.\\COM%d"), portnr);///可以显示COM10以上端口//add by itas109 2014-01-09

    // stop is index 0 = 1 1=1.5 2=2
    int mystop;
    int myparity;
    switch (stopbits)
    {
    case 0:
        mystop = ONESTOPBIT;
        break;
    case 1:
        mystop = ONE5STOPBITS;
        break;
    case 2:
        mystop = TWOSTOPBITS;
        break;
        //增加默认情况,因为stopbits=1.5时,SetCommState会报错。
        //一般的电脑串口不支持1.5停止位,这个1.5停止位似乎用在红外传输上的。
        //by itas109 20160506
    default:
        mystop = ONESTOPBIT;
        break;
    }
    myparity = 0;
    parity = _totupper(parity);
    switch (parity)
    {
    case _T('N'):
        myparity = 0;
        break;
    case _T('O'):
        myparity = 1;
        break;
    case _T('E'):
        myparity = 2;
        break;
    case _T('M'):
        myparity = 3;
        break;
    case _T('S'):
        myparity = 4;
        break;
        //增加默认情况。
        //by itas109 20160506
    default:
        myparity = 0;
        break;
    }
    _stprintf_s(szBaud, MAX_PATH, _T("baud=%d parity=%c data=%d stop=%d"), baud, parity, databits, mystop);

    // get a handle to the port
    /*
    通信程序在CreateFile处指定串口设备及相关的操作属性,再返回一个句柄,
    该句柄将被用于后续的通信操作,并贯穿整个通信过程串口打开后,其属性
    被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&&dcb)读取
    当前串口设备控制块DCB设置,修改后通过SetCommState(hComm,&&dcb)将其写
    入。运用ReadFile()与WriteFile()这两个API函数实现串口读写操作,若为异
    步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读
    写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING,
    表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject()
    来等待结束信号并设置最长等待时间
    */
    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)
    {
        //add by itas109 2016-08-02
        //串口打开失败,增加提示信息
        switch (GetLastError())
        {
            //串口不存在
        case ERROR_FILE_NOT_FOUND:
        {
                                     TCHAR Temp[200] = { 0 };
                                     _stprintf_s(Temp, 200, _T("COM%d ERROR_FILE_NOT_FOUND,Error Code:%d"), portnr, GetLastError());
                                     MessageBox(NULL, Temp, _T("COM InitPort Error"), MB_ICONERROR);
                                     break;
        }
            //串口拒绝访问
        case ERROR_ACCESS_DENIED:
        {
                                    TCHAR Temp[200] = { 0 };
                                    _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,Error Code:%d"), portnr, GetLastError());
                                    MessageBox(NULL, Temp, _T("COM InitPort Error"), MB_ICONERROR);
                                    break;
        }
        default:
            break;
        }
        // port not found
        delete[] szPort;
        delete[] szBaud;

        return FALSE;
    }

    // set the timeout values
    ///设置超时
    m_SerialPortTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
    m_SerialPortTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
    m_SerialPortTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
    m_SerialPortTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
    m_SerialPortTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000;

    // configure
    ///配置
    ///分别调用Windows API设置串口参数
    if (SetCommTimeouts(m_hComm, &m_SerialPortTimeouts))///设置超时
    {
        /*
        若对端口数据的响应时间要求较严格,可采用事件驱动方式。
        事件驱动方式通过设置事件通知,当所希望的事件发生时,Windows
        发出该事件已发生的通知,这与DOS环境下的中断方式很相似。Windows
        定义了9种串口通信事件,较常用的有以下三种:
        EV_RXCHAR:接收到一个字节,并放入输入缓冲区;
        EV_TXEMPTY:输出缓冲区中的最后一个字符,发送出去;
        EV_RXFLAG:接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区
        在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事
        件的发生。SetCommMask(hComm,0)可使WaitCommEvent()中止
        */
        if (SetCommMask(m_hComm, dwCommEvents))///设置通信事件
        {

            if (GetCommState(m_hComm, &m_dcb))///获取当前DCB参数
            {
                m_dcb.EvtChar = 'q';
                m_dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS bit high!
                m_dcb.BaudRate = baud;  // add by mrlong
                m_dcb.Parity = myparity;
                m_dcb.ByteSize = databits;
                m_dcb.StopBits = mystop;

                //if (BuildCommDCB(szBaud &m_dcb))///填写DCB结构
                //{
                if (SetCommState(m_hComm, &m_dcb))///配置DCB
                    ; // normal operation... continue
                else
                    ProcessErrorMessage(_T("SetCommState()"));
                //}
                //else
                //    ProcessErrorMessage("BuildCommDCB()");
            }
            else
                ProcessErrorMessage(_T("GetCommState()"));
        }
        else
            ProcessErrorMessage(_T("SetCommMask()"));
    }
    else
        ProcessErrorMessage(_T("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.
///线程函数
///监视线程的大致流程:
///检查串口-->进入循环{WaitCommEvent(不阻塞询问)询问事件-->如果有事件来到-->到相应处理(关闭\读\写)}
//
DWORD WINAPI 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.
    ///TRUE表示线程正在运行
    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!

        /*
        WaitCommEvent函数第3个参数1pOverlapped可以是一个OVERLAPPED结构的变量指针
        ,也可以是NULL,当用NULL时,表示该函数是同步的,否则表示该函数是异步的。
        调用WaitCommEvent时,如果异步操作不能立即完成,会立即返回FALSE,系统在
        WaitCommEvent返回前将OVERLAPPED结构成员hEvent设为无信号状态,等到产生通信
        事件时,系统将其置有信号
        */

        bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);///表示该函数是异步的

        if (!bResult)
        {
            // If WaitCommEvent() returns FALSE, process the last error to determin
            // the reason..
            ///如果WaitCommEvent返回Error为FALSE,则查询错误信息
            switch (dwError = GetLastError())
            {
            case ERROR_IO_PENDING:     ///正常情况,没有字符可读 erroe code:997
            {
                                        // This is a normal return value if there are no bytes
                                        // to read at the port.
                                        // Do nothing and continue
                                        break;
            }
            case ERROR_INVALID_PARAMETER:///系统错误 erroe code: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;
            }
            case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
            {
                                         port->m_hComm = INVALID_HANDLE_VALUE;
                                         TCHAR Temp[200] = { 0 };
                                         _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,WaitCommEvent() Error Code:%d"), port->m_nPortNr, GetLastError());
                                         MessageBox(NULL, Temp, _T("COM WaitCommEvent Error"), MB_ICONERROR);
                                         break;
            }
            case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
            {
                                          port->m_hComm = INVALID_HANDLE_VALUE;
                                          break;
            }
            case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
            {
                                       port->m_hComm = INVALID_HANDLE_VALUE;
                                       TCHAR Temp[200] = { 0 };
                                       _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,WaitCommEvent() Error Code:%d"), port->m_nPortNr, GetLastError());
                                       MessageBox(NULL, Temp, _T("COM WaitCommEvent Error"), MB_ICONERROR);
                                       break;
            }
            default:///发生其他错误,其中有串口读写中断开串口连接的错误(错误22)
            {
                        // All other error codes indicate a serious error has
                        //发生错误时,将串口句柄置为无效句柄
                        port->m_hComm = INVALID_HANDLE_VALUE;
                        // occured.  Process this error.
                        port->ProcessErrorMessage(_T("WaitCommEvent()"));
                        break;
            }
            }
        }
        else    ///WaitCommEvent()能正确返回
        {
            // 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.
        ///等待3个事件:关断/读/写,有一个事件发生就返回
        Event = WaitForMultipleObjects(3, ///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.
                  ///关断事件,关闭串口
                  CloseHandle(port->m_hComm);
                  port->m_hComm = NULL;
                  port->m_bThreadAlive = FALSE;

                  // Kill this thread.  break is not needed, but makes me feel better.
                  //AfxEndThread(100);
                  ::ExitThread(100);

                  break;
        }
        case 1: // write event 发送数据
        {
                    // Write character event from port
                    WriteChar(port);
                    break;
        }
        case 2:    // read event 将定义的各种消息发送出去
        {
                    GetCommMask(port->m_hComm, &CommEvent);
                    if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
                    {
                        if (IsReceiveString == 1)
                        {
                            ReceiveStr(port);//多字符接收
                        }
                        else if (IsReceiveString == 0)
                        {
                            ReceiveChar(port);//单字符接收
                        }
                        else
                        {
                            //默认多字符接收
                            ReceiveStr(port);//多字符接收
                        }
                    }

                    if (CommEvent & EV_CTS) //CTS信号状态发生变化
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_CTS_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中 
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_RXFLAG_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_BREAK)  //输入中发生中断
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_BREAK_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY 
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_ERR_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
                    if (CommEvent & EV_RING) //检测到振铃指示
                        ::SendMessage(port->m_pOwner, Wm_SerialPort_RING_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);

                    break;
        }
        default:
        {
                   MessageBox(NULL, _T("Receive Error!"), _T("COM Receive Error"), MB_ICONERROR);
                   break;
        }

        } // end switch

    } // close forever loop

    return 0;
}

//
// start comm watching
///开启监视线程
//
BOOL CSerialPort::StartMonitoring()
{
    //if (!(m_Thread = AfxBeginThread(CommThread, this)))
    if (!(m_Thread = ::CreateThread(NULL, 0, CommThread, this, 0, NULL)))
        return FALSE;
    //TRACE("Thread started\n");
    return TRUE;
}

//
// Restart the comm thread
///从挂起恢复监视线程
//
BOOL CSerialPort::ResumeMonitoring()
{
    //TRACE("Thread resumed\n");
    //m_Thread->ResumeThread();
    ::ResumeThread(m_Thread);
    return TRUE;
}

//
// Suspend the comm thread
///挂起监视线程
//
BOOL CSerialPort::SuspendMonitoring()
{
    //TRACE("Thread suspended\n");
    //m_Thread->SuspendThread();
    ::SuspendThread(m_Thread);
    return TRUE;
}

BOOL CSerialPort::IsThreadSuspend(HANDLE hThread)
{
    DWORD   count = SuspendThread(hThread);
    if (count == -1)
    {
        return FALSE;
    }
    ResumeThread(hThread);
    return (count != 0);
}

//
// If there is a error, give the right message
///如果有错误,给出提示
//
void CSerialPort::ProcessErrorMessage(TCHAR* ErrorText)
{
    TCHAR Temp[200] = { 0 };

    LPVOID lpMsgBuf;

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR)&lpMsgBuf,
        0,
        NULL
        );

    _stprintf_s(Temp, 200, _T("WARNING:  %s Failed with the following error: \n%s\nPort: %d\n"), ErrorText, (TCHAR*)lpMsgBuf, m_nPortNr);
    MessageBox(NULL, Temp, _T("Application Error"), MB_ICONSTOP);

    LocalFree(lpMsgBuf);
}

//
// Write a character.
//
void CSerialPort::WriteChar(CSerialPort* port)
{
    BOOL bWrite = TRUE;
    BOOL bResult = TRUE;

    DWORD BytesSent = 0;
    DWORD SendLen = port->m_nWriteSize;
    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
            SendLen,    // add by mrlong
            //strlen((char*)port->m_szWriteBuffer),    // 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;
            }
            case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
            {
                                         port->m_hComm = INVALID_HANDLE_VALUE;
                                         TCHAR Temp[200] = { 0 };
                                         _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,WriteFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                         MessageBox(NULL, Temp, _T("COM WriteFile Error"), MB_ICONERROR);
                                         break;
            }
            case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
            {
                                          port->m_hComm = INVALID_HANDLE_VALUE;
                                          break;
            }
            case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
            {
                                       port->m_hComm = INVALID_HANDLE_VALUE;
                                       TCHAR Temp[200] = { 0 };
                                       _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,WriteFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                       MessageBox(NULL, Temp, _T("COM WriteFile Error"), MB_ICONERROR);
                                       break;
            }
            default:
            {
                       // all other error codes
                       port->ProcessErrorMessage(_T("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(_T("GetOverlappedResults() in WriteFile()"));
        }
    } // end if (!bWrite)

    // Verify that the data size send equals what we tried to send
    //if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by 
    //{
    //TRACE(_T("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n"), BytesSent, _tcsclen((TCHAR*)port->m_szWriteBuffer));
    //}
}

//
// Character received. Inform the owner
//
void CSerialPort::ReceiveChar(CSerialPort* port)
{
    BOOL  bRead = TRUE;
    BOOL  bResult = TRUE;
    DWORD dwError = 0;
    DWORD BytesRead = 0;
    COMSTAT comstat;
    unsigned char RXBuff;

    for (;;)
    {
        //add by liquanhai 2011-11-06  防止死锁
        if (WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)
            return;

        // Gain ownership of the comm port critical section.
        // This process guarantees no other part of this program 
        // is using the port object. 

        EnterCriticalSection(&port->m_csCommunicationSync);

        // ClearCommError() will update the COMSTAT structure and
        // clear any other errors.
        ///更新COMSTAT

        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();
                                         ///异步IO仍在进行
                                         bRead = FALSE;
                                         break;
                }
                case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
                {
                                             port->m_hComm = INVALID_HANDLE_VALUE;
                                             TCHAR Temp[200] = { 0 };
                                             _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                             MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                             break;
                }
                case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
                {
                                              port->m_hComm = INVALID_HANDLE_VALUE;
                                              break;
                }
                case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
                {
                                           port->m_hComm = INVALID_HANDLE_VALUE;
                                           TCHAR Temp[200] = { 0 };
                                           _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                           MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                           break;
                }
                default:
                {
                           // Another error has occured.  Process this error.
                           port->ProcessErrorMessage(_T("ReadFile()"));
                           break;
                           //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多
                }
                }
            }
            else///ReadFile返回TRUE
            {
                // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
                bRead = TRUE;
            }
        }  // close if (bRead)

        ///异步IO操作仍在进行,需要调用GetOverlappedResult查询
        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(_T("GetOverlappedResults() in ReadFile()"));
            }
        }  // close if (!bRead)

        LeaveCriticalSection(&port->m_csCommunicationSync);

        // notify parent that a byte was received
        //避免线程互相等待,产生死锁,使用PostMessage()代替SendMessage()
        PostMessage(port->m_pOwner, Wm_SerialPort_RXCHAR, (WPARAM)RXBuff, (LPARAM)port->m_nPortNr);
        //::SendMessage((port->m_pOwner), Wm_SerialPort_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
    } // end forever loop

}

//
// str received. Inform the owner
//
void CSerialPort::ReceiveStr(CSerialPort* port)
{
    BOOL  bRead = TRUE;
    BOOL  bResult = TRUE;
    DWORD dwError = 0;
    DWORD BytesRead = 0;
    COMSTAT comstat;
    serialPortInfo commInfo;

    for (;;)
    {
        //add by liquanhai 2011-11-06  防止死锁
        if (WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)
            return;

        // Gain ownership of the comm port critical section.
        // This process guarantees no other part of this program 
        // is using the port object. 

        EnterCriticalSection(&port->m_csCommunicationSync);

        // ClearCommError() will update the COMSTAT structure and
        // clear any other errors.
        ///更新COMSTAT

        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.

        ///所有字符均被读出,中断循环
        //0xcccccccc表示串口异常了,会导致RXBuff指针初始化错误
        if (comstat.cbInQue == 0 || comstat.cbInQue == 0xcccccccc)
        {
            // break out when all bytes have been read
            break;
        }

        //如果遇到'\0',那么数据会被截断,实际数据全部读取只是没有显示完全,这个时候使用memcpy才能全部获取
        unsigned char* RXBuff = new unsigned char[comstat.cbInQue + 1];
        if (RXBuff == NULL)
        {
            return;
        }
        RXBuff[comstat.cbInQue] = '\0';//附加字符串结束符

        EnterCriticalSection(&port->m_csCommunicationSync);

        if (bRead)
        {
            ///串口读出,读出缓冲区中字节
            bResult = ReadFile(port->m_hComm,        // Handle to COMM port 
                RXBuff,                // RX Buffer Pointer
                comstat.cbInQue,                    // Read cbInQue len 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();
                                         ///异步IO仍在进行
                                         bRead = FALSE;
                                         break;
                }
                case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
                {
                                             port->m_hComm = INVALID_HANDLE_VALUE;
                                             TCHAR Temp[200] = { 0 };
                                             _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                             MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                             break;
                }
                case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
                {
                                              port->m_hComm = INVALID_HANDLE_VALUE;
                                              break;
                }
                case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
                {
                                           port->m_hComm = INVALID_HANDLE_VALUE;
                                           TCHAR Temp[200] = { 0 };
                                           _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
                                           MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
                                           break;
                }
                default:
                {
                           // Another error has occured.  Process this error.
                           port->ProcessErrorMessage(_T("ReadFile()"));
                           break;
                           //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多
                }
                }
            }
            else///ReadFile返回TRUE
            {
                // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
                bRead = TRUE;
            }
        }  // close if (bRead)

        ///异步IO操作仍在进行,需要调用GetOverlappedResult查询
        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(_T("GetOverlappedResults() in ReadFile()"));
            }
        }  // close if (!bRead)

        LeaveCriticalSection(&port->m_csCommunicationSync);

        commInfo.portNr = port->m_nPortNr;
        commInfo.bytesRead = BytesRead;
        // notify parent that some byte was received
        ::SendMessage((port->m_pOwner), Wm_SerialPort_RXSTR, (WPARAM)RXBuff, (LPARAM)&commInfo);

        //释放
        delete[] RXBuff;
        RXBuff = NULL;

    } // end forever loop

}

//
// 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;
}

BOOL CSerialPort::IsOpen()
{
    return m_hComm != NULL && m_hComm != INVALID_HANDLE_VALUE;//m_hComm增加INVALID_HANDLE_VALUE的情况 add by itas109 2016-07-29
}

void CSerialPort::ClosePort()
{
    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    //串口句柄无效  add by itas109 2016-07-29
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
        return;
    }

    do
    {
        SetEvent(m_hShutdownEvent);
        //add by liquanhai  防止死锁  2011-11-06
        if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
        {
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
        }
    } while (m_bThreadAlive);

    // if the port is still opened: close it 
    if (m_hComm != NULL)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
    }

    // Close Handles  
    if (m_hShutdownEvent != NULL)
    {
        ResetEvent(m_hShutdownEvent);
    }
    if (m_ov.hEvent != NULL)
    {
        ResetEvent(m_ov.hEvent);
    }
    if (m_hWriteEvent != NULL)
    {
        ResetEvent(m_hWriteEvent);
        //CloseHandle(m_hWriteEvent);//开发者反映,这里会导致多个串口工作时,重新打开串口异常
    }

    if (m_szWriteBuffer != NULL)
    {
        delete[] m_szWriteBuffer;
        m_szWriteBuffer = NULL;
    }
}


void CSerialPort::WriteToPort(char* string, size_t 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(BYTE* Buffer, size_t n)
{
    assert(m_hComm != 0);
    memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
    memcpy(m_szWriteBuffer, Buffer, n);
    /*
    int i;
    for(i=0; i<n; i++)
    {
    m_szWriteBuffer[i] = Buffer[i];
    }
    */
    m_nWriteSize = n;

    // set event for write
    SetEvent(m_hWriteEvent);
}

///查询注册表的串口号,将值存于数组中
///本代码参考于mingojiang的获取串口逻辑名代码
//
void CSerialPort::QueryKey(HKEY hKey)
{
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
    //    TCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
    //    DWORD    cbName;                   // size of name string 
    TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name 
    DWORD    cchClassName = MAX_PATH;  // size of class string 
    DWORD    cSubKeys = 0;               // number of subkeys 
    DWORD    cbMaxSubKey;              // longest subkey size 
    DWORD    cchMaxClass;              // longest class string 
    DWORD    cValues;              // number of values for key 
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data 
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 

    DWORD i, retCode;

    TCHAR  achValue[MAX_VALUE_NAME];
    DWORD cchValue = MAX_VALUE_NAME;

    // Get the class name and the value count. 
    retCode = RegQueryInfoKey(
        hKey,                    // key handle 
        achClass,                // buffer for class name 
        &cchClassName,           // size of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 

    for (i = 0; i<20; i++)///存放串口号的数组初始化
    {
        m_nComArray[i] = -1;
    }

    // Enumerate the key values. 
    if (cValues > 0) {
        for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++)
        {
            cchValue = MAX_VALUE_NAME;
            achValue[0] = '\0';
            if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL, NULL, NULL))
            {
                TCHAR strDSName[10];
                memset(strDSName, 0, 10);
                DWORD nValueType = 0, nBuffLen = 10;
                if (ERROR_SUCCESS == RegQueryValueEx(hKey, (LPCTSTR)achValue, NULL, &nValueType, (LPBYTE)strDSName, &nBuffLen))
                {
                    int nIndex = -1;
                    while (++nIndex < MaxSerialPortNum)
                    {
                        if (-1 == m_nComArray[nIndex])
                        {
                            m_nComArray[nIndex] = _tstoi((TCHAR*)(strDSName + 3));
                            break;
                        }
                    }
                }
            }
        }
    }
    else{
        MessageBox(NULL, _T("No Com In This Computer!"), _T("COM Query Error"), MB_ICONERROR);
    }

}

#ifdef _AFX
void CSerialPort::Hkey2ComboBox(CComboBox& m_PortNO)
{
    HKEY hTestKey;
    bool Flag = FALSE;

    ///仅是XP系统的注册表位置,其他系统根据实际情况做修改
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_READ, &hTestKey))
    {
        QueryKey(hTestKey);
    }
    RegCloseKey(hTestKey);

    int i = 0;
    m_PortNO.ResetContent();///刷新时,清空下拉列表内容
    while (i < MaxSerialPortNum && -1 != m_nComArray[i])
    {
        CString szCom;
        szCom.Format(_T("COM%d"), m_nComArray[i]);
        m_PortNO.InsertString(i, szCom.GetBuffer(5));
        ++i;
        Flag = TRUE;
        if (Flag)///把第一个发现的串口设为下拉列表的默认值
            m_PortNO.SetCurSel(0);
    }
}
#endif // _AFX

[转载 自](https://blog.youkuaiyun.com/c344935/article/details/81133308)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值