zoukankan      html  css  js  c++  java
  • CSerialPort串口类最新修正版(解决关闭死锁问题)2014-01-11

    这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等。
    Remon提供的串口类网址为: http://codeguru.earthweb.com/network/serialport.shtml
    由于已经运行十几年了,原文的问答部分列出来这么多年来的问题,经过网友们的总结,补充和修改原来代码后,整理出一份相对比较完美的代码。

    此外还附带一份小项目的源代码,它超越了串口助手,给人一种耳目一新的感觉。亮点如下:

    1. 它解决了串口关闭时出现死锁不响应问题,可以直接用到开发的项目上。
    2. 并且是扩展了的串口助手,具有通信协议编辑和使用功能,
    3. 软件升级检测,值得做软件升级例子使用。
    4. 最重要的是源代码很不错,很值得大家去研究。

       分享给需要的朋友们,有问题,可以在留言,以便做进一步修正。

    VC串口修正类应用的小项目下载地址:http://download.csdn.net/detail/liquanhai/3763088

    2014-01-11补充说明:

    最新的CSerialPort类的下载地址在:https://github.com/liquanhai/CSerialPort



    头文件CSerialPort.h如下:

    1. /* 
    2. **  FILENAME            CSerialPort.h 
    3. ** 
    4. **  PURPOSE             This class can read, write and watch one serial port. 
    5. **                      It sends messages to its owner when something happends on the port 
    6. **                      The class creates a thread for reading and writing so the main 
    7. **                      program is not blocked. 
    8. ** 
    9. **  CREATION DATE       15-09-1997 
    10. **  LAST MODIFICATION   12-11-1997 
    11. ** 
    12. **  AUTHOR              Remon Spekreijse 
    13. ** 
    14. ** 
    15. ************************************************************************************ 
    16. **  author: mrlong date:2007-12-25 
    17. ** 
    18. **  改进 
    19. **    1) 增加ClosePort 
    20. **    2) 增加 writetoProt() 两个方法 
    21. **    3) 增加 SendData 与 RecvData 方法 
    22. ************************************************************************************** 
    23. *************************************************************************************** 
    24. **  author:liquanhai date:2011-11-04 
    25. **  改进 
    26. **    1)增加 ClosePort中交出控制权,防止死锁问题 
    27. **    2) 增加 ReceiveChar中防止线程死锁 
    28.  */  
    29.   
    30. #ifndef __SERIALPORT_H__   
    31. #define __SERIALPORT_H__   
    32.   
    33. #define WM_COMM_BREAK_DETECTED      WM_USER+1   // A break was detected on input.   
    34. #define WM_COMM_CTS_DETECTED        WM_USER+2   // The CTS (clear-to-send) signal changed state.    
    35. #define WM_COMM_DSR_DETECTED        WM_USER+3   // The DSR (data-set-ready) signal changed state.    
    36. #define WM_COMM_ERR_DETECTED        WM_USER+4   // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.    
    37. #define WM_COMM_RING_DETECTED       WM_USER+5   // A ring indicator was detected.    
    38. #define WM_COMM_RLSD_DETECTED       WM_USER+6   // The RLSD (receive-line-signal-detect) signal changed state.    
    39. #define WM_COMM_RXCHAR              WM_USER+7   // A character was received and placed in the input buffer.    
    40. #define WM_COMM_RXFLAG_DETECTED     WM_USER+8   // The event character was received and placed in the input buffer.     
    41. #define WM_COMM_TXEMPTY_DETECTED    WM_USER+9   // The last character in the output buffer was sent.     
    42.   
    43. class CSerialPort  
    44. {                                                          
    45. public:  
    46.     // contruction and destruction   
    47.     CSerialPort();  
    48.     virtual     ~CSerialPort();  
    49.   
    50.     // port initialisation                                             
    51.     BOOL        InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200,   
    52.                 char parity = 'N'UINT databits = 8, UINT stopsbits = 1,   
    53.                 DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,  
    54.               
    55.                 DWORD ReadIntervalTimeout = 1000,  
    56.                 DWORD ReadTotalTimeoutMultiplier = 1000,  
    57.                 DWORD ReadTotalTimeoutConstant = 1000,  
    58.                 DWORD WriteTotalTimeoutMultiplier = 1000,  
    59.                 DWORD WriteTotalTimeoutConstant = 1000);  
    60.   
    61.     // start/stop comm watching   
    62.     BOOL        StartMonitoring();  
    63.     BOOL        RestartMonitoring();  
    64.     BOOL        StopMonitoring();  
    65.   
    66.     DWORD       GetWriteBufferSize();  
    67.     DWORD       GetCommEvents();  
    68.     DCB         GetDCB();  
    69.   
    70.     void        WriteToPort(char* string);  
    71.     void        WriteToPort(char* string,int n); // add by mrlong 2007-12-25   
    72.     void        WriteToPort(LPCTSTR string);     // add by mrlong 2007-12-25   
    73.     void        WriteToPort(BYTE* Buffer, int n);// add by mrlong   
    74.     void        ClosePort();                     // add by mrlong 2007-12-2     
    75.   
    76.     void SendData(LPCTSTR lpszData, const int nLength);   //串口发送函数 by mrlong 2008-2-15   
    77.     BOOL RecvData(LPTSTR lpszData, const int nSize);      //串口接收函数 by mrlong 2008-2-15   
    78.   
    79. protected:  
    80.     // protected memberfunctions   
    81.     void        ProcessErrorMessage(char* ErrorText);  
    82.     static UINT CommThread(LPVOID pParam);  
    83.     static void ReceiveChar(CSerialPort* port, COMSTAT comstat);  
    84.     static void WriteChar(CSerialPort* port);  
    85.   
    86.     // thread   
    87.     CWinThread*         m_Thread;  
    88.   
    89.     // synchronisation objects   
    90.     CRITICAL_SECTION    m_csCommunicationSync;  
    91.     BOOL                m_bThreadAlive;  
    92.   
    93.     // handles   
    94.     HANDLE              m_hShutdownEvent;  //stop发生的事件   
    95.     HANDLE              m_hComm;           // read     
    96.     HANDLE              m_hWriteEvent;     // write   
    97.   
    98.     // Event array.    
    99.     // One element is used for each event. There are two event handles for each port.   
    100.     // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).   
    101.     // There is a general shutdown when the port is closed.    
    102.     HANDLE              m_hEventArray[3];  
    103.   
    104.     // structures   
    105.     OVERLAPPED          m_ov;  
    106.     COMMTIMEOUTS        m_CommTimeouts;  
    107.     DCB                 m_dcb;  
    108.   
    109.     // owner window   
    110.     CWnd*               m_pOwner;  
    111.   
    112.     // misc   
    113.     UINT                m_nPortNr;        //?????   
    114.     char*               m_szWriteBuffer;  
    115.     DWORD               m_dwCommEvents;  
    116.     DWORD               m_nWriteBufferSize;  
    117.   
    118.     int                 m_nWriteSize; //add by mrlong 2007-12-25   
    119. };  
    120.   
    121. #endif __SERIALPORT_H__  
    /*
    **	FILENAME			CSerialPort.h
    **
    **	PURPOSE				This class can read, write and watch one serial port.
    **						It sends messages to its owner when something happends on the port
    **						The class creates a thread for reading and writing so the main
    **						program is not blocked.
    **
    **	CREATION DATE		15-09-1997
    **	LAST MODIFICATION	12-11-1997
    **
    **	AUTHOR				Remon Spekreijse
    **
    **
    ************************************************************************************
    **  author: mrlong date:2007-12-25
    **
    **  改进
    **    1) 增加ClosePort
    **    2) 增加 writetoProt() 两个方法
    **    3) 增加 SendData 与 RecvData 方法
    **************************************************************************************
    ***************************************************************************************
    **  author:liquanhai date:2011-11-04
    **  改进
    **    1)增加 ClosePort中交出控制权,防止死锁问题
    **    2) 增加 ReceiveChar中防止线程死锁
     */
    
    #ifndef __SERIALPORT_H__
    #define __SERIALPORT_H__
    
    #define WM_COMM_BREAK_DETECTED		WM_USER+1	// A break was detected on input.
    #define WM_COMM_CTS_DETECTED		WM_USER+2	// The CTS (clear-to-send) signal changed state. 
    #define WM_COMM_DSR_DETECTED		WM_USER+3	// The DSR (data-set-ready) signal changed state. 
    #define WM_COMM_ERR_DETECTED		WM_USER+4	// A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 
    #define WM_COMM_RING_DETECTED		WM_USER+5	// A ring indicator was detected. 
    #define WM_COMM_RLSD_DETECTED		WM_USER+6	// The RLSD (receive-line-signal-detect) signal changed state. 
    #define WM_COMM_RXCHAR				WM_USER+7	// A character was received and placed in the input buffer. 
    #define WM_COMM_RXFLAG_DETECTED		WM_USER+8	// The event character was received and placed in the input buffer.  
    #define WM_COMM_TXEMPTY_DETECTED	WM_USER+9	// The last character in the output buffer was sent.  
    
    class CSerialPort
    {														 
    public:
    	// contruction and destruction
    	CSerialPort();
    	virtual		~CSerialPort();
    
    	// port initialisation											
    	BOOL		InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200, 
    				char parity = 'N', UINT databits = 8, UINT stopsbits = 1, 
    				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		RestartMonitoring();
    	BOOL		StopMonitoring();
    
    	DWORD		GetWriteBufferSize();
    	DWORD		GetCommEvents();
    	DCB			GetDCB();
    
    	void		WriteToPort(char* string);
    	void        WriteToPort(char* string,int n); // add by mrlong 2007-12-25
    	void		WriteToPort(LPCTSTR string);     // add by mrlong 2007-12-25
    	void        WriteToPort(BYTE* Buffer, int n);// add by mrlong
    	void        ClosePort();                     // add by mrlong 2007-12-2  
    
    	void SendData(LPCTSTR lpszData, const int nLength);   //串口发送函数 by mrlong 2008-2-15
    	BOOL RecvData(LPTSTR lpszData, const int nSize);      //串口接收函数 by mrlong 2008-2-15
    
    protected:
    	// protected memberfunctions
    	void		ProcessErrorMessage(char* ErrorText);
    	static UINT	CommThread(LPVOID pParam);
    	static void	ReceiveChar(CSerialPort* port, COMSTAT comstat);
    	static void	WriteChar(CSerialPort* port);
    
    	// thread
    	CWinThread*			m_Thread;
    
    	// synchronisation objects
    	CRITICAL_SECTION	m_csCommunicationSync;
    	BOOL				m_bThreadAlive;
    
    	// handles
    	HANDLE				m_hShutdownEvent;  //stop发生的事件
    	HANDLE				m_hComm;           // read  
    	HANDLE				m_hWriteEvent;     // write
    
    	// Event array. 
    	// One element is used for each event. There are two event handles for each port.
    	// A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
    	// There is a general shutdown when the port is closed. 
    	HANDLE				m_hEventArray[3];
    
    	// structures
    	OVERLAPPED			m_ov;
    	COMMTIMEOUTS		m_CommTimeouts;
    	DCB					m_dcb;
    
    	// owner window
    	CWnd*				m_pOwner;
    
    	// misc
    	UINT				m_nPortNr;        //?????
    	char*				m_szWriteBuffer;
    	DWORD				m_dwCommEvents;
    	DWORD				m_nWriteBufferSize;
    
    	int                 m_nWriteSize; //add by mrlong 2007-12-25
    };
    
    #endif __SERIALPORT_H__



    源文件cpp文件如下:

    1. /* 
    2. **  FILENAME            CSerialPort.cpp 
    3. ** 
    4. **  PURPOSE             This class can read, write and watch one serial port. 
    5. **                      It sends messages to its owner when something happends on the port 
    6. **                      The class creates a thread for reading and writing so the main 
    7. **                      program is not blocked. 
    8. ** 
    9. **  CREATION DATE       15-09-1997 
    10. **  LAST MODIFICATION   12-11-1997 
    11. ** 
    12. **  AUTHOR              Remon Spekreijse 
    13. ** 
    14. ** 
    15. */  
    16.   
    17. #include "stdafx.h"   
    18. #include "SerialPort.h"   
    19.   
    20. #include <assert.h>   
    21.    
    22. //   
    23. // Constructor   
    24. //   
    25. CSerialPort::CSerialPort()  
    26. {  
    27.     m_hComm = NULL;  
    28.   
    29.     // initialize overlapped structure members to zero   
    30.     m_ov.Offset = 0;  
    31.     m_ov.OffsetHigh = 0;  
    32.   
    33.     // create events   
    34.     m_ov.hEvent = NULL;  
    35.     m_hWriteEvent = NULL;  
    36.     m_hShutdownEvent = NULL;  
    37.   
    38.     m_szWriteBuffer = NULL;  
    39.   
    40.     m_bThreadAlive = FALSE;  
    41.     m_nWriteSize = 1;  
    42. }  
    43.   
    44. //   
    45. // Delete dynamic memory   
    46. //   
    47. CSerialPort::~CSerialPort()  
    48. {  
    49.     do  
    50.     {  
    51.         SetEvent(m_hShutdownEvent);  
    52.     } while (m_bThreadAlive);  
    53.   
    54.     if (m_hComm != NULL)  
    55.     {  
    56.         CloseHandle(m_hComm);  
    57.         m_hComm = NULL;  
    58.     }  
    59.     // Close Handles     
    60.     if(m_hShutdownEvent!=NULL)  
    61.         CloseHandle( m_hShutdownEvent);   
    62.     if(m_ov.hEvent!=NULL)  
    63.         CloseHandle( m_ov.hEvent );   
    64.     if(m_hWriteEvent!=NULL)  
    65.         CloseHandle( m_hWriteEvent );   
    66.   
    67.     TRACE("Thread ended ");  
    68.   
    69.     delete [] m_szWriteBuffer;  
    70. }  
    71.   
    72. //   
    73. // Initialize the port. This can be port 1 to 4.   
    74. //   
    75. //   
    76. //parity:   
    77. //  n=none   
    78. //  e=even   
    79. //  o=odd   
    80. //  m=mark   
    81. //  s=space   
    82. //data:   
    83. //  5,6,7,8   
    84. //stop:   
    85. //  1,1.5,2    
    86. //   
    87. BOOL CSerialPort::InitPort(CWnd* pPortOwner,    // the owner (CWnd) of the port (receives message)   
    88.                            UINT  portnr,        // portnumber (1..4)   
    89.                            UINT  baud,          // baudrate   
    90.                            char  parity,        // parity    
    91.                            UINT  databits,      // databits    
    92.                            UINT  stopbits,      // stopbits    
    93.                            DWORD dwCommEvents,  // EV_RXCHAR, EV_CTS etc   
    94.                            UINT  writebuffersize,// size to the writebuffer   
    95.                              
    96.                            DWORD   ReadIntervalTimeout,  
    97.                            DWORD   ReadTotalTimeoutMultiplier,  
    98.                            DWORD   ReadTotalTimeoutConstant,  
    99.                            DWORD   WriteTotalTimeoutMultiplier,  
    100.                            DWORD   WriteTotalTimeoutConstant )    
    101.   
    102. {  
    103.     assert(portnr > 0 && portnr < 5);  
    104.     assert(pPortOwner != NULL);  
    105.   
    106.     // if the thread is alive: Kill   
    107.     if (m_bThreadAlive)  
    108.     {  
    109.         do  
    110.         {  
    111.             SetEvent(m_hShutdownEvent);  
    112.         } while (m_bThreadAlive);  
    113.         TRACE("Thread ended ");  
    114.     }  
    115.   
    116.     // create events   
    117.     if (m_ov.hEvent != NULL)  
    118.         ResetEvent(m_ov.hEvent);  
    119.     else  
    120.         m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
    121.   
    122.     if (m_hWriteEvent != NULL)  
    123.         ResetEvent(m_hWriteEvent);  
    124.     else  
    125.         m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
    126.       
    127.     if (m_hShutdownEvent != NULL)  
    128.         ResetEvent(m_hShutdownEvent);  
    129.     else  
    130.         m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
    131.   
    132.     // initialize the event objects   
    133.     m_hEventArray[0] = m_hShutdownEvent;    // highest priority   
    134.     m_hEventArray[1] = m_ov.hEvent;  
    135.     m_hEventArray[2] = m_hWriteEvent;  
    136.   
    137.     // initialize critical section   
    138.     InitializeCriticalSection(&m_csCommunicationSync);  
    139.       
    140.     // set buffersize for writing and save the owner   
    141.     m_pOwner = pPortOwner;  
    142.   
    143.     if (m_szWriteBuffer != NULL)  
    144.         delete [] m_szWriteBuffer;  
    145.     m_szWriteBuffer = new char[writebuffersize];  
    146.   
    147.     m_nPortNr = portnr;  
    148.   
    149.     m_nWriteBufferSize = writebuffersize;  
    150.     m_dwCommEvents = dwCommEvents;  
    151.   
    152.     BOOL bResult = FALSE;  
    153.     char *szPort = new char[50];  
    154.     char *szBaud = new char[50];  
    155.   
    156.     // now it critical!   
    157.     EnterCriticalSection(&m_csCommunicationSync);  
    158.   
    159.     // if the port is already opened: close it   
    160.     if (m_hComm != NULL)  
    161.     {  
    162.         CloseHandle(m_hComm);  
    163.         m_hComm = NULL;  
    164.     }  
    165.   
    166.     // prepare port strings   
    167.     sprintf(szPort, "COM%d", portnr);  
    168.     // stop is index 0 = 1 1=1.5 2=2   
    169.     int mystop;  
    170.     int myparity;  
    171.     switch(stopbits)  
    172.     {  
    173.         case 0:  
    174.             mystop = ONESTOPBIT;  
    175.             break;  
    176.         case 1:  
    177.             mystop = ONE5STOPBITS;  
    178.             break;  
    179.         case 2:  
    180.             mystop = TWOSTOPBITS;  
    181.             break;  
    182.     }  
    183.     myparity = 0;  
    184.     switch(parity)  
    185.     {  
    186.         case 'N':  
    187.             myparity = 0;  
    188.             break;  
    189.         case 'E':  
    190.             myparity = 1;  
    191.             break;  
    192.         case 'O':  
    193.             myparity = 2;  
    194.             break;  
    195.         case 'M':  
    196.             myparity = 3;  
    197.             break;  
    198.         case 'S':  
    199.             myparity = 4;  
    200.             break;  
    201.     }  
    202.     sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);  
    203.   
    204.     // get a handle to the port   
    205.     m_hComm = CreateFile(szPort,                        // communication port string (COMX)   
    206.                          GENERIC_READ | GENERIC_WRITE,  // read/write types   
    207.                          0,                             // comm devices must be opened with exclusive access   
    208.                          NULL,                          // no security attributes   
    209.                          OPEN_EXISTING,                 // comm devices must use OPEN_EXISTING   
    210.                          FILE_FLAG_OVERLAPPED,          // Async I/O   
    211.                          0);                            // template must be 0 for comm devices   
    212.   
    213.     if (m_hComm == INVALID_HANDLE_VALUE)  
    214.     {  
    215.         // port not found   
    216.         delete [] szPort;  
    217.         delete [] szBaud;  
    218.   
    219.         return FALSE;  
    220.     }  
    221.   
    222.     // set the timeout values   
    223.     m_CommTimeouts.ReadIntervalTimeout         = ReadIntervalTimeout * 1000;  
    224.     m_CommTimeouts.ReadTotalTimeoutMultiplier  = ReadTotalTimeoutMultiplier * 1000;  
    225.     m_CommTimeouts.ReadTotalTimeoutConstant    = ReadTotalTimeoutConstant * 1000;  
    226.     m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;  
    227.     m_CommTimeouts.WriteTotalTimeoutConstant   = WriteTotalTimeoutConstant * 1000;  
    228.   
    229.     // configure   
    230.     if (SetCommTimeouts(m_hComm, &m_CommTimeouts))  
    231.     {                            
    232.         if (SetCommMask(m_hComm, dwCommEvents))  
    233.         {  
    234.             if (GetCommState(m_hComm, &m_dcb))  
    235.             {  
    236.                 m_dcb.EvtChar = 'q';  
    237.                 m_dcb.fRtsControl = RTS_CONTROL_ENABLE;     // set RTS bit high!   
    238.                 m_dcb.BaudRate = baud;  // add by mrlong   
    239.                 m_dcb.Parity   = myparity;  
    240.                 m_dcb.ByteSize = databits;  
    241.                 m_dcb.StopBits = mystop;  
    242.                           
    243.                 //if (BuildCommDCB(szBaud, &m_dcb))   
    244.                 //{   
    245.                     if (SetCommState(m_hComm, &m_dcb))  
    246.                         ; // normal operation... continue   
    247.                     else  
    248.                         ProcessErrorMessage("SetCommState()");  
    249.                 //}   
    250.                 //else   
    251.                 //  ProcessErrorMessage("BuildCommDCB()");   
    252.             }  
    253.             else  
    254.                 ProcessErrorMessage("GetCommState()");  
    255.         }  
    256.         else  
    257.             ProcessErrorMessage("SetCommMask()");  
    258.     }  
    259.     else  
    260.         ProcessErrorMessage("SetCommTimeouts()");  
    261.   
    262.     delete [] szPort;  
    263.     delete [] szBaud;  
    264.   
    265.     // flush the port   
    266.     PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
    267.   
    268.     // release critical section   
    269.     LeaveCriticalSection(&m_csCommunicationSync);  
    270.   
    271.     TRACE("Initialisation for communicationport %d completed. Use Startmonitor to communicate. ", portnr);  
    272.   
    273.     return TRUE;  
    274. }  
    275.   
    276. //   
    277. //  The CommThread Function.   
    278. //   
    279. UINT CSerialPort::CommThread(LPVOID pParam)  
    280. {  
    281.     // Cast the void pointer passed to the thread back to   
    282.     // a pointer of CSerialPort class   
    283.     CSerialPort *port = (CSerialPort*)pParam;  
    284.       
    285.     // Set the status variable in the dialog class to   
    286.     // TRUE to indicate the thread is running.   
    287.     port->m_bThreadAlive = TRUE;   
    288.           
    289.     // Misc. variables   
    290.     DWORD BytesTransfered = 0;   
    291.     DWORD Event = 0;  
    292.     DWORD CommEvent = 0;  
    293.     DWORD dwError = 0;  
    294.     COMSTAT comstat;  
    295.     BOOL  bResult = TRUE;  
    296.           
    297.     // Clear comm buffers at startup   
    298.     if (port->m_hComm)       // check if the port is opened   
    299.         PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
    300.   
    301.     // begin forever loop.  This loop will run as long as the thread is alive.   
    302.     for (;;)   
    303.     {   
    304.   
    305.         // Make a call to WaitCommEvent().  This call will return immediatly   
    306.         // because our port was created as an async port (FILE_FLAG_OVERLAPPED   
    307.         // and an m_OverlappedStructerlapped structure specified).  This call will cause the    
    308.         // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to    
    309.         // be placed in a non-signeled state if there are no bytes available to be read,   
    310.         // or to a signeled state if there are bytes available.  If this event handle    
    311.         // is set to the non-signeled state, it will be set to signeled when a    
    312.         // character arrives at the port.   
    313.   
    314.         // we do this for each port!   
    315.   
    316.         bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);  
    317.   
    318.         if (!bResult)    
    319.         {   
    320.             // If WaitCommEvent() returns FALSE, process the last error to determin   
    321.             // the reason..   
    322.             switch (dwError = GetLastError())   
    323.             {   
    324.             case ERROR_IO_PENDING:    
    325.                 {   
    326.                     // This is a normal return value if there are no bytes   
    327.                     // to read at the port.   
    328.                     // Do nothing and continue   
    329.                     break;  
    330.                 }  
    331.             case 87:  
    332.                 {  
    333.                     // Under Windows NT, this value is returned for some reason.   
    334.                     // I have not investigated why, but it is also a valid reply   
    335.                     // Also do nothing and continue.   
    336.                     break;  
    337.                 }  
    338.             default:  
    339.                 {  
    340.                     // All other error codes indicate a serious error has   
    341.                     // occured.  Process this error.   
    342.                     port->ProcessErrorMessage("WaitCommEvent()");  
    343.                     break;  
    344.                 }  
    345.             }  
    346.         }  
    347.         else  
    348.         {  
    349.             // If WaitCommEvent() returns TRUE, check to be sure there are   
    350.             // actually bytes in the buffer to read.     
    351.             //   
    352.             // If you are reading more than one byte at a time from the buffer    
    353.             // (which this program does not do) you will have the situation occur    
    354.             // where the first byte to arrive will cause the WaitForMultipleObjects()    
    355.             // function to stop waiting.  The WaitForMultipleObjects() function    
    356.             // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state   
    357.             // as it returns.     
    358.             //   
    359.             // If in the time between the reset of this event and the call to    
    360.             // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again   
    361.             // to the signeled state. When the call to ReadFile() occurs, it will    
    362.             // read all of the bytes from the buffer, and the program will   
    363.             // loop back around to WaitCommEvent().   
    364.             //    
    365.             // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,   
    366.             // but there are no bytes available to read.  If you proceed and call   
    367.             // ReadFile(), it will return immediatly due to the async port setup, but   
    368.             // GetOverlappedResults() will not return until the next character arrives.   
    369.             //   
    370.             // It is not desirable for the GetOverlappedResults() function to be in    
    371.             // this state.  The thread shutdown event (event 0) and the WriteFile()   
    372.             // event (Event2) will not work if the thread is blocked by GetOverlappedResults().   
    373.             //   
    374.             // The solution to this is to check the buffer with a call to ClearCommError().   
    375.             // This call will reset the event handle, and if there are no bytes to read   
    376.             // we can loop back through WaitCommEvent() again, then proceed.   
    377.             // If there are really bytes to read, do nothing and proceed.   
    378.           
    379.             bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
    380.   
    381.             if (comstat.cbInQue == 0)  
    382.                 continue;  
    383.         }   // end if bResult   
    384.   
    385.         // Main wait function.  This function will normally block the thread   
    386.         // until one of nine events occur that require action.   
    387.         Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);  
    388.   
    389.         switch (Event)  
    390.         {  
    391.         case 0:  
    392.             {  
    393.                 // Shutdown event.  This is event zero so it will be   
    394.                 // the higest priority and be serviced first.   
    395.   
    396.                 port->m_bThreadAlive = FALSE;  
    397.                   
    398.                 // Kill this thread.  break is not needed, but makes me feel better.   
    399.                 AfxEndThread(100);  
    400.                 break;  
    401.             }  
    402.         case 1: // read event   
    403.             {  
    404.                 GetCommMask(port->m_hComm, &CommEvent);  
    405.                 if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中    
    406.                     ReceiveChar(port, comstat);  
    407.                   
    408.                 if (CommEvent & EV_CTS) //CTS信号状态发生变化   
    409.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
    410.                 if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中    
    411.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
    412.                 if (CommEvent & EV_BREAK)  //输入中发生中断   
    413.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
    414.                 if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY    
    415.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
    416.                 if (CommEvent & EV_RING) //检测到振铃指示   
    417.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
    418.                       
    419.                 break;  
    420.             }    
    421.         case 2: // write event   
    422.             {  
    423.                 // Write character event from port   
    424.                 WriteChar(port);  
    425.                 break;  
    426.             }  
    427.   
    428.         } // end switch   
    429.   
    430.     } // close forever loop   
    431.   
    432.     return 0;  
    433. }  
    434.   
    435. //   
    436. // start comm watching   
    437. //   
    438. BOOL CSerialPort::StartMonitoring()  
    439. {  
    440.     if (!(m_Thread = AfxBeginThread(CommThread, this)))  
    441.         return FALSE;  
    442.     TRACE("Thread started ");  
    443.     return TRUE;      
    444. }  
    445.   
    446. //   
    447. // Restart the comm thread   
    448. //   
    449. BOOL CSerialPort::RestartMonitoring()  
    450. {  
    451.     TRACE("Thread resumed ");  
    452.     m_Thread->ResumeThread();  
    453.     return TRUE;      
    454. }  
    455.   
    456.   
    457. //   
    458. // Suspend the comm thread   
    459. //   
    460. BOOL CSerialPort::StopMonitoring()  
    461. {  
    462.     TRACE("Thread suspended ");  
    463.     m_Thread->SuspendThread();   
    464.     return TRUE;      
    465. }  
    466.   
    467.   
    468. //   
    469. // If there is a error, give the right message   
    470. //   
    471. void CSerialPort::ProcessErrorMessage(char* ErrorText)  
    472. {  
    473.     char *Temp = new char[200];  
    474.       
    475.     LPVOID lpMsgBuf;  
    476.   
    477.     FormatMessage(   
    478.         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,  
    479.         NULL,  
    480.         GetLastError(),  
    481.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language   
    482.         (LPTSTR) &lpMsgBuf,  
    483.         0,  
    484.         NULL   
    485.     );  
    486.   
    487.     sprintf(Temp, "WARNING:  %s Failed with the following error:  %s Port: %d ", (char*)ErrorText, lpMsgBuf, m_nPortNr);   
    488.     MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);  
    489.   
    490.     LocalFree(lpMsgBuf);  
    491.     delete[] Temp;  
    492. }  
    493.   
    494. //   
    495. // Write a character.   
    496. //   
    497. void CSerialPort::WriteChar(CSerialPort* port)  
    498. {  
    499.     BOOL bWrite = TRUE;  
    500.     BOOL bResult = TRUE;  
    501.   
    502.     DWORD BytesSent = 0;  
    503.     DWORD SendLen   = port->m_nWriteSize;  
    504.     ResetEvent(port->m_hWriteEvent);  
    505.   
    506.   
    507.     // Gain ownership of the critical section   
    508.     EnterCriticalSection(&port->m_csCommunicationSync);  
    509.   
    510.     if (bWrite)  
    511.     {  
    512.         // Initailize variables   
    513.         port->m_ov.Offset = 0;  
    514.         port->m_ov.OffsetHigh = 0;  
    515.        
    516.         // Clear buffer   
    517.         PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
    518.   
    519.         bResult = WriteFile(port->m_hComm,                           // Handle to COMM Port   
    520.                             port->m_szWriteBuffer,                   // Pointer to message buffer in calling finction   
    521.                             SendLen,    // add by mrlong   
    522.                             //strlen((char*)port->m_szWriteBuffer),  // Length of message to send   
    523.                             &BytesSent,                             // Where to store the number of bytes sent   
    524.                             &port->m_ov);                            // Overlapped structure   
    525.   
    526.         // deal with any error codes   
    527.         if (!bResult)    
    528.         {  
    529.             DWORD dwError = GetLastError();  
    530.             switch (dwError)  
    531.             {  
    532.                 case ERROR_IO_PENDING:  
    533.                     {  
    534.                         // continue to GetOverlappedResults()   
    535.                         BytesSent = 0;  
    536.                         bWrite = FALSE;  
    537.                         break;  
    538.                     }  
    539.                 default:  
    540.                     {  
    541.                         // all other error codes   
    542.                         port->ProcessErrorMessage("WriteFile()");  
    543.                     }  
    544.             }  
    545.         }   
    546.         else  
    547.         {  
    548.             LeaveCriticalSection(&port->m_csCommunicationSync);  
    549.         }  
    550.     } // end if(bWrite)   
    551.   
    552.     if (!bWrite)  
    553.     {  
    554.         bWrite = TRUE;  
    555.       
    556.         bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port    
    557.                                       &port->m_ov,       // Overlapped structure   
    558.                                       &BytesSent,       // Stores number of bytes sent   
    559.                                       TRUE);            // Wait flag   
    560.   
    561.         LeaveCriticalSection(&port->m_csCommunicationSync);  
    562.   
    563.         // deal with the error code    
    564.         if (!bResult)    
    565.         {  
    566.             port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");  
    567.         }     
    568.     } // end if (!bWrite)   
    569.   
    570.     // Verify that the data size send equals what we tried to send   
    571.     if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by    
    572.     {  
    573.         TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d ", BytesSent, strlen((char*)port->m_szWriteBuffer));  
    574.     }  
    575. }  
    576.   
    577. //   
    578. // Character received. Inform the owner   
    579. //   
    580. void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)  
    581. {  
    582.     BOOL  bRead = TRUE;   
    583.     BOOL  bResult = TRUE;  
    584.     DWORD dwError = 0;  
    585.     DWORD BytesRead = 0;  
    586.     unsigned char RXBuff;  
    587.   
    588.     for (;;)   
    589.     {   
    590.              //add by liquanhai 2011-11-06  防止死锁   
    591.              if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)  
    592.                  return;  
    593.   
    594.                 // Gain ownership of the comm port critical section.   
    595.         // This process guarantees no other part of this program    
    596.         // is using the port object.    
    597.           
    598.         EnterCriticalSection(&port->m_csCommunicationSync);  
    599.   
    600.         // ClearCommError() will update the COMSTAT structure and   
    601.         // clear any other errors.   
    602.           
    603.         bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
    604.   
    605.         LeaveCriticalSection(&port->m_csCommunicationSync);  
    606.   
    607.         // start forever loop.  I use this type of loop because I   
    608.         // do not know at runtime how many loops this will have to   
    609.         // run. My solution is to start a forever loop and to   
    610.         // break out of it when I have processed all of the   
    611.         // data available.  Be careful with this approach and   
    612.         // be sure your loop will exit.   
    613.         // My reasons for this are not as clear in this sample    
    614.         // as it is in my production code, but I have found this    
    615.         // solutiion to be the most efficient way to do this.   
    616.           
    617.         if (comstat.cbInQue == 0)  
    618.         {  
    619.             // break out when all bytes have been read   
    620.             break;  
    621.         }  
    622.                           
    623.         EnterCriticalSection(&port->m_csCommunicationSync);  
    624.   
    625.         if (bRead)  
    626.         {  
    627.             bResult = ReadFile(port->m_hComm,        // Handle to COMM port    
    628.                                &RXBuff,             // RX Buffer Pointer   
    629.                                1,                   // Read one byte   
    630.                                &BytesRead,          // Stores number of bytes read   
    631.                                &port->m_ov);     // pointer to the m_ov structure   
    632.             // deal with the error code    
    633.             if (!bResult)    
    634.             {   
    635.                 switch (dwError = GetLastError())   
    636.                 {   
    637.                     case ERROR_IO_PENDING:    
    638.                         {   
    639.                             // asynchronous i/o is still in progress    
    640.                             // Proceed on to GetOverlappedResults();   
    641.                             bRead = FALSE;  
    642.                             break;  
    643.                         }  
    644.                     default:  
    645.                         {  
    646.                             // Another error has occured.  Process this error.   
    647.                             port->ProcessErrorMessage("ReadFile()");  
    648.                             break;  
    649.                         }   
    650.                 }  
    651.             }  
    652.             else  
    653.             {  
    654.                 // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()   
    655.                 bRead = TRUE;  
    656.             }  
    657.         }  // close if (bRead)   
    658.   
    659.         if (!bRead)  
    660.         {  
    661.             bRead = TRUE;  
    662.             bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port    
    663.                                           &port->m_ov,       // Overlapped structure   
    664.                                           &BytesRead,       // Stores number of bytes read   
    665.                                           TRUE);            // Wait flag   
    666.   
    667.             // deal with the error code    
    668.             if (!bResult)    
    669.             {  
    670.                 port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");  
    671.             }     
    672.         }  // close if (!bRead)   
    673.                   
    674.         LeaveCriticalSection(&port->m_csCommunicationSync);  
    675.   
    676.         // notify parent that a byte was received   
    677.         ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);  
    678.     } // end forever loop   
    679.   
    680. }  
    681.   
    682. //   
    683. // Write a string to the port   
    684. //   
    685. void CSerialPort::WriteToPort(char* string)  
    686. {         
    687.     assert(m_hComm != 0);  
    688.   
    689.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
    690.     strcpy(m_szWriteBuffer, string);  
    691.     m_nWriteSize=strlen(string); // add by mrlong   
    692.     // set event for write   
    693.     SetEvent(m_hWriteEvent);  
    694. }  
    695.   
    696. //   
    697. // Return the device control block   
    698. //   
    699. DCB CSerialPort::GetDCB()  
    700. {  
    701.     return m_dcb;  
    702. }  
    703.   
    704. //   
    705. // Return the communication event masks   
    706. //   
    707. DWORD CSerialPort::GetCommEvents()  
    708. {  
    709.     return m_dwCommEvents;  
    710. }  
    711.   
    712. //   
    713. // Return the output buffer size   
    714. //   
    715. DWORD CSerialPort::GetWriteBufferSize()  
    716. {  
    717.     return m_nWriteBufferSize;  
    718. }  
    719.   
    720. void CSerialPort::ClosePort()  
    721. {  
    /*
    **	FILENAME			CSerialPort.cpp
    **
    **	PURPOSE				This class can read, write and watch one serial port.
    **						It sends messages to its owner when something happends on the port
    **						The class creates a thread for reading and writing so the main
    **						program is not blocked.
    **
    **	CREATION DATE		15-09-1997
    **	LAST MODIFICATION	12-11-1997
    **
    **	AUTHOR				Remon Spekreijse
    **
    **
    */
    
    #include "stdafx.h"
    #include "SerialPort.h"
    
    #include <assert.h>
     
    //
    // Constructor
    //
    CSerialPort::CSerialPort()
    {
    	m_hComm = NULL;
    
    	// initialize overlapped structure members to zero
    	m_ov.Offset = 0;
    	m_ov.OffsetHigh = 0;
    
    	// create events
    	m_ov.hEvent = NULL;
    	m_hWriteEvent = NULL;
    	m_hShutdownEvent = NULL;
    
    	m_szWriteBuffer = NULL;
    
    	m_bThreadAlive = FALSE;
    	m_nWriteSize = 1;
    }
    
    //
    // Delete dynamic memory
    //
    CSerialPort::~CSerialPort()
    {
    	do
    	{
    		SetEvent(m_hShutdownEvent);
    	} while (m_bThreadAlive);
    
    	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
    ");
    
    	delete [] m_szWriteBuffer;
    }
    
    //
    // Initialize the port. This can be port 1 to 4.
    //
    //
    //parity:
    //  n=none
    //  e=even
    //  o=odd
    //  m=mark
    //  s=space
    //data:
    //  5,6,7,8
    //stop:
    //  1,1.5,2 
    //
    BOOL CSerialPort::InitPort(CWnd* pPortOwner,	// the owner (CWnd) of the port (receives message)
    						   UINT  portnr,		// portnumber (1..4)
    						   UINT  baud,			// baudrate
    						   char  parity,		// parity 
    						   UINT  databits,		// databits 
    						   UINT  stopbits,		// stopbits 
    						   DWORD dwCommEvents,	// EV_RXCHAR, EV_CTS etc
    						   UINT  writebuffersize,// size to the writebuffer
    						   
    						   DWORD   ReadIntervalTimeout,
    						   DWORD   ReadTotalTimeoutMultiplier,
    						   DWORD   ReadTotalTimeoutConstant,
    						   DWORD   WriteTotalTimeoutMultiplier,
    						   DWORD   WriteTotalTimeoutConstant )	
    
    {
    	assert(portnr > 0 && portnr < 5);
    	assert(pPortOwner != NULL);
    
    	// if the thread is alive: Kill
    	if (m_bThreadAlive)
    	{
    		do
    		{
    			SetEvent(m_hShutdownEvent);
    		} while (m_bThreadAlive);
    		TRACE("Thread ended
    ");
    	}
    
    	// create events
    	if (m_ov.hEvent != NULL)
    		ResetEvent(m_ov.hEvent);
    	else
    		m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    	if (m_hWriteEvent != NULL)
    		ResetEvent(m_hWriteEvent);
    	else
    		m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    	
    	if (m_hShutdownEvent != NULL)
    		ResetEvent(m_hShutdownEvent);
    	else
    		m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    	// initialize the event objects
    	m_hEventArray[0] = m_hShutdownEvent;	// highest priority
    	m_hEventArray[1] = m_ov.hEvent;
    	m_hEventArray[2] = m_hWriteEvent;
    
    	// initialize critical section
    	InitializeCriticalSection(&m_csCommunicationSync);
    	
    	// set buffersize for writing and save the owner
    	m_pOwner = pPortOwner;
    
    	if (m_szWriteBuffer != NULL)
    		delete [] m_szWriteBuffer;
    	m_szWriteBuffer = new char[writebuffersize];
    
    	m_nPortNr = portnr;
    
    	m_nWriteBufferSize = writebuffersize;
    	m_dwCommEvents = dwCommEvents;
    
    	BOOL bResult = FALSE;
    	char *szPort = new char[50];
    	char *szBaud = new char[50];
    
    	// now it critical!
    	EnterCriticalSection(&m_csCommunicationSync);
    
    	// if the port is already opened: close it
    	if (m_hComm != NULL)
    	{
    		CloseHandle(m_hComm);
    		m_hComm = NULL;
    	}
    
    	// prepare port strings
    	sprintf(szPort, "COM%d", portnr);
    	// 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;
    	}
    	myparity = 0;
    	switch(parity)
    	{
    		case 'N':
    			myparity = 0;
    			break;
    		case 'E':
    			myparity = 1;
    			break;
    		case 'O':
    			myparity = 2;
    			break;
    		case 'M':
    			myparity = 3;
    			break;
    		case 'S':
    			myparity = 4;
    			break;
    	}
    	sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);
    
    	// get a handle to the port
    	m_hComm = CreateFile(szPort,						// communication port string (COMX)
    					     GENERIC_READ | GENERIC_WRITE,	// read/write types
    					     0,								// comm devices must be opened with exclusive access
    					     NULL,							// no security attributes
    					     OPEN_EXISTING,					// comm devices must use OPEN_EXISTING
    					     FILE_FLAG_OVERLAPPED,			// Async I/O
    					     0);							// template must be 0 for comm devices
    
    	if (m_hComm == INVALID_HANDLE_VALUE)
    	{
    		// port not found
    		delete [] szPort;
    		delete [] szBaud;
    
    		return FALSE;
    	}
    
    	// set the timeout values
    	m_CommTimeouts.ReadIntervalTimeout         = ReadIntervalTimeout * 1000;
    	m_CommTimeouts.ReadTotalTimeoutMultiplier  = ReadTotalTimeoutMultiplier * 1000;
    	m_CommTimeouts.ReadTotalTimeoutConstant    = ReadTotalTimeoutConstant * 1000;
    	m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
    	m_CommTimeouts.WriteTotalTimeoutConstant   = WriteTotalTimeoutConstant * 1000;
    
    	// configure
    	if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
    	{						   
    		if (SetCommMask(m_hComm, dwCommEvents))
    		{
    			if (GetCommState(m_hComm, &m_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))
    				//{
    					if (SetCommState(m_hComm, &m_dcb))
    						; // normal operation... continue
    					else
    						ProcessErrorMessage("SetCommState()");
    				//}
    				//else
    				//	ProcessErrorMessage("BuildCommDCB()");
    			}
    			else
    				ProcessErrorMessage("GetCommState()");
    		}
    		else
    			ProcessErrorMessage("SetCommMask()");
    	}
    	else
    		ProcessErrorMessage("SetCommTimeouts()");
    
    	delete [] szPort;
    	delete [] szBaud;
    
    	// flush the port
    	PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
    
    	// release critical section
    	LeaveCriticalSection(&m_csCommunicationSync);
    
    	TRACE("Initialisation for communicationport %d completed.
    Use Startmonitor to communicate.
    ", portnr);
    
    	return TRUE;
    }
    
    //
    //  The CommThread Function.
    //
    UINT CSerialPort::CommThread(LPVOID pParam)
    {
    	// Cast the void pointer passed to the thread back to
    	// a pointer of CSerialPort class
    	CSerialPort *port = (CSerialPort*)pParam;
    	
    	// Set the status variable in the dialog class to
    	// TRUE to indicate the thread is running.
    	port->m_bThreadAlive = TRUE;	
    		
    	// Misc. variables
    	DWORD BytesTransfered = 0; 
    	DWORD Event = 0;
    	DWORD CommEvent = 0;
    	DWORD dwError = 0;
    	COMSTAT comstat;
    	BOOL  bResult = TRUE;
    		
    	// Clear comm buffers at startup
    	if (port->m_hComm)		// check if the port is opened
    		PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
    
    	// begin forever loop.  This loop will run as long as the thread is alive.
    	for (;;) 
    	{ 
    
    		// Make a call to WaitCommEvent().  This call will return immediatly
    		// because our port was created as an async port (FILE_FLAG_OVERLAPPED
    		// and an m_OverlappedStructerlapped structure specified).  This call will cause the 
    		// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to 
    		// be placed in a non-signeled state if there are no bytes available to be read,
    		// or to a signeled state if there are bytes available.  If this event handle 
    		// is set to the non-signeled state, it will be set to signeled when a 
    		// character arrives at the port.
    
    		// we do this for each port!
    
    		bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
    
    		if (!bResult)  
    		{ 
    			// If WaitCommEvent() returns FALSE, process the last error to determin
    			// the reason..
    			switch (dwError = GetLastError()) 
    			{ 
    			case ERROR_IO_PENDING: 	
    				{ 
    					// This is a normal return value if there are no bytes
    					// to read at the port.
    					// Do nothing and continue
    					break;
    				}
    			case 87:
    				{
    					// Under Windows NT, this value is returned for some reason.
    					// I have not investigated why, but it is also a valid reply
    					// Also do nothing and continue.
    					break;
    				}
    			default:
    				{
    					// All other error codes indicate a serious error has
    					// occured.  Process this error.
    					port->ProcessErrorMessage("WaitCommEvent()");
    					break;
    				}
    			}
    		}
    		else
    		{
    			// If WaitCommEvent() returns TRUE, check to be sure there are
    			// actually bytes in the buffer to read.  
    			//
    			// If you are reading more than one byte at a time from the buffer 
    			// (which this program does not do) you will have the situation occur 
    			// where the first byte to arrive will cause the WaitForMultipleObjects() 
    			// function to stop waiting.  The WaitForMultipleObjects() function 
    			// resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
    			// as it returns.  
    			//
    			// If in the time between the reset of this event and the call to 
    			// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
    			// to the signeled state. When the call to ReadFile() occurs, it will 
    			// read all of the bytes from the buffer, and the program will
    			// loop back around to WaitCommEvent().
    			// 
    			// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
    			// but there are no bytes available to read.  If you proceed and call
    			// ReadFile(), it will return immediatly due to the async port setup, but
    			// GetOverlappedResults() will not return until the next character arrives.
    			//
    			// It is not desirable for the GetOverlappedResults() function to be in 
    			// this state.  The thread shutdown event (event 0) and the WriteFile()
    			// event (Event2) will not work if the thread is blocked by GetOverlappedResults().
    			//
    			// The solution to this is to check the buffer with a call to ClearCommError().
    			// This call will reset the event handle, and if there are no bytes to read
    			// we can loop back through WaitCommEvent() again, then proceed.
    			// If there are really bytes to read, do nothing and proceed.
    		
    			bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
    
    			if (comstat.cbInQue == 0)
    				continue;
    		}	// end if bResult
    
    		// Main wait function.  This function will normally block the thread
    		// until one of nine events occur that require action.
    		Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
    
    		switch (Event)
    		{
    		case 0:
    			{
    				// Shutdown event.  This is event zero so it will be
    				// the higest priority and be serviced first.
    
    			 	port->m_bThreadAlive = FALSE;
    				
    				// Kill this thread.  break is not needed, but makes me feel better.
    				AfxEndThread(100);
    				break;
    			}
    		case 1:	// read event
    			{
    				GetCommMask(port->m_hComm, &CommEvent);
    				if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中 
    					ReceiveChar(port, comstat);
    				
    				if (CommEvent & EV_CTS) //CTS信号状态发生变化
    					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
    				if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中 
    					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
    				if (CommEvent & EV_BREAK)  //输入中发生中断
    					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
    				if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY 
    					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
    				if (CommEvent & EV_RING) //检测到振铃指示
    					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
    					
    				break;
    			}  
    		case 2: // write event
    			{
    				// Write character event from port
    				WriteChar(port);
    				break;
    			}
    
    		} // end switch
    
    	} // close forever loop
    
    	return 0;
    }
    
    //
    // start comm watching
    //
    BOOL CSerialPort::StartMonitoring()
    {
    	if (!(m_Thread = AfxBeginThread(CommThread, this)))
    		return FALSE;
    	TRACE("Thread started
    ");
    	return TRUE;	
    }
    
    //
    // Restart the comm thread
    //
    BOOL CSerialPort::RestartMonitoring()
    {
    	TRACE("Thread resumed
    ");
    	m_Thread->ResumeThread();
    	return TRUE;	
    }
    
    
    //
    // Suspend the comm thread
    //
    BOOL CSerialPort::StopMonitoring()
    {
    	TRACE("Thread suspended
    ");
    	m_Thread->SuspendThread(); 
    	return TRUE;	
    }
    
    
    //
    // If there is a error, give the right message
    //
    void CSerialPort::ProcessErrorMessage(char* ErrorText)
    {
    	char *Temp = new char[200];
    	
    	LPVOID lpMsgBuf;
    
    	FormatMessage( 
    		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    		NULL,
    		GetLastError(),
    		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    		(LPTSTR) &lpMsgBuf,
    		0,
    		NULL 
    	);
    
    	sprintf(Temp, "WARNING:  %s Failed with the following error: 
    %s
    Port: %d
    ", (char*)ErrorText, lpMsgBuf, m_nPortNr); 
    	MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
    
    	LocalFree(lpMsgBuf);
    	delete[] Temp;
    }
    
    //
    // Write a character.
    //
    void CSerialPort::WriteChar(CSerialPort* port)
    {
    	BOOL bWrite = TRUE;
    	BOOL bResult = TRUE;
    
    	DWORD BytesSent = 0;
    	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;
    					}
    				default:
    					{
    						// all other error codes
    						port->ProcessErrorMessage("WriteFile()");
    					}
    			}
    		} 
    		else
    		{
    			LeaveCriticalSection(&port->m_csCommunicationSync);
    		}
    	} // end if(bWrite)
    
    	if (!bWrite)
    	{
    		bWrite = TRUE;
    	
    		bResult = GetOverlappedResult(port->m_hComm,	// Handle to COMM port 
    									  &port->m_ov,		// Overlapped structure
    									  &BytesSent,		// Stores number of bytes sent
    									  TRUE); 			// Wait flag
    
    		LeaveCriticalSection(&port->m_csCommunicationSync);
    
    		// deal with the error code 
    		if (!bResult)  
    		{
    			port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
    		}	
    	} // end if (!bWrite)
    
    	// Verify that the data size send equals what we tried to send
    	if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by 
    	{
    		TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d
    ", BytesSent, strlen((char*)port->m_szWriteBuffer));
    	}
    }
    
    //
    // Character received. Inform the owner
    //
    void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)
    {
    	BOOL  bRead = TRUE; 
    	BOOL  bResult = TRUE;
    	DWORD dwError = 0;
    	DWORD BytesRead = 0;
    	unsigned char RXBuff;
    
    	for (;;) 
    	{ 
                 //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.
    		
    		bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
    
    		LeaveCriticalSection(&port->m_csCommunicationSync);
    
    		// start forever loop.  I use this type of loop because I
    		// do not know at runtime how many loops this will have to
    		// run. My solution is to start a forever loop and to
    		// break out of it when I have processed all of the
    		// data available.  Be careful with this approach and
    		// be sure your loop will exit.
    		// My reasons for this are not as clear in this sample 
    		// as it is in my production code, but I have found this 
    		// solutiion to be the most efficient way to do this.
    		
    		if (comstat.cbInQue == 0)
    		{
    			// break out when all bytes have been read
    			break;
    		}
    						
    		EnterCriticalSection(&port->m_csCommunicationSync);
    
    		if (bRead)
    		{
    			bResult = ReadFile(port->m_hComm,		// Handle to COMM port 
    							   &RXBuff,				// RX Buffer Pointer
    							   1,					// Read one byte
    							   &BytesRead,			// Stores number of bytes read
    							   &port->m_ov);		// pointer to the m_ov structure
    			// deal with the error code 
    			if (!bResult)  
    			{ 
    				switch (dwError = GetLastError()) 
    				{ 
    					case ERROR_IO_PENDING: 	
    						{ 
    							// asynchronous i/o is still in progress 
    							// Proceed on to GetOverlappedResults();
    							bRead = FALSE;
    							break;
    						}
    					default:
    						{
    							// Another error has occured.  Process this error.
    							port->ProcessErrorMessage("ReadFile()");
    							break;
    						} 
    				}
    			}
    			else
    			{
    				// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
    				bRead = TRUE;
    			}
    		}  // close if (bRead)
    
    		if (!bRead)
    		{
    			bRead = TRUE;
    			bResult = GetOverlappedResult(port->m_hComm,	// Handle to COMM port 
    										  &port->m_ov,		// Overlapped structure
    										  &BytesRead,		// Stores number of bytes read
    										  TRUE); 			// Wait flag
    
    			// deal with the error code 
    			if (!bResult)  
    			{
    				port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
    			}	
    		}  // close if (!bRead)
    				
    		LeaveCriticalSection(&port->m_csCommunicationSync);
    
    		// notify parent that a byte was received
    		::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
    	} // end forever loop
    
    }
    
    //
    // Write a string to the port
    //
    void CSerialPort::WriteToPort(char* string)
    {		
    	assert(m_hComm != 0);
    
    	memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
    	strcpy(m_szWriteBuffer, string);
    	m_nWriteSize=strlen(string); // add by mrlong
    	// set event for write
    	SetEvent(m_hWriteEvent);
    }
    
    //
    // Return the device control block
    //
    DCB CSerialPort::GetDCB()
    {
    	return m_dcb;
    }
    
    //
    // Return the communication event masks
    //
    DWORD CSerialPort::GetCommEvents()
    {
    	return m_dwCommEvents;
    }
    
    //
    // Return the output buffer size
    //
    DWORD CSerialPort::GetWriteBufferSize()
    {
    	return m_nWriteBufferSize;
    }
    
    void CSerialPort::ClosePort()
    {
    1.         MSG message;  
    2.     do  
    3.     {  
    4.           SetEvent(m_hShutdownEvent);  
    5.               if(::PeekMessage(&message,m_pOwner->m_hWnd,0,0,PM_REMOVE))  
    6.                {  
    7.                ::TranslateMessage(&message);  
    8.                 ::DispatchMessage(&message);  
    9.               }  
    10.                  
    11.          } while (m_bThreadAlive);  
    12.   
    13.     // if the port is still opened: close it    
    14.     if (m_hComm != NULL)  
    15.     {  
    16.         CloseHandle(m_hComm);  
    17.         m_hComm = NULL;  
    18.     }  
    19.       
    20.     // Close Handles     
    21.     if(m_hShutdownEvent!=NULL)  
    22.         ResetEvent(m_hShutdownEvent);  
    23.     if(m_ov.hEvent!=NULL)  
    24.         ResetEvent(m_ov.hEvent);  
    25.     if(m_hWriteEvent!=NULL)  
    26.         ResetEvent(m_hWriteEvent);            
    27.           
    28.     //delete [] m_szWriteBuffer;   
    29.       
    30. }  
    31.   
    32. void CSerialPort::WriteToPort(char* string,int n)  
    33. {  
    34.     assert(m_hComm != 0);  
    35.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
    36.     memcpy(m_szWriteBuffer, string, n);  
    37.     m_nWriteSize = n;  
    38.   
    39.     // set event for write   
    40.     SetEvent(m_hWriteEvent);  
    41. }  
    42.   
    43. void CSerialPort::WriteToPort(LPCTSTR string)  
    44. {  
    45.     assert(m_hComm != 0);  
    46.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
    47.     strcpy(m_szWriteBuffer, string);  
    48.     m_nWriteSize=strlen(string);  
    49.     // set event for write   
    50.     SetEvent(m_hWriteEvent);  
    51. }  
    52.   
    53. void CSerialPort::WriteToPort(BYTE* Buffer, int n)  
    54. {  
    55.     assert(m_hComm != 0);  
    56.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
    57.     int i;  
    58.     for(i=0; i<n; i++)  
    59.     {  
    60.         m_szWriteBuffer[i] = Buffer[i];  
    61.     }  
    62.     m_nWriteSize=n;  
    63.   
    64.     // set event for write   
    65.     SetEvent(m_hWriteEvent);  
    66. }  
    67.   
    68.   
    69. void CSerialPort::SendData(LPCTSTR lpszData, const int nLength)  
    70. {  
    71.     assert(m_hComm != 0);  
    72.     memset(m_szWriteBuffer, 0, nLength);  
    73.     strcpy(m_szWriteBuffer, lpszData);  
    74.     m_nWriteSize=nLength;  
    75.     // set event for write   
    76.     SetEvent(m_hWriteEvent);  
    77. }  
    78.   
    79. BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize)  
    80. {  
    81.     //   
    82.     //接收数据   
    83.     //   
    84.     assert(m_hComm!=0);  
    85.     memset(lpszData,0,nSize);  
    86.     DWORD mylen  = 0;  
    87.     DWORD mylen2 = 0;  
    88.     while (mylen<nSize) {  
    89.         if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL))   
    90.             return FALSE;  
    91.         mylen += mylen2;  
    92.   
    93.           
    94.     }  
    95.       
    96.     return TRUE;  
    97. }  
            MSG message;
    	do
    	{
    	      SetEvent(m_hShutdownEvent);
                  if(::PeekMessage(&message,m_pOwner->m_hWnd,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);			
    		
    	//delete [] m_szWriteBuffer;
    	
    }
    
    void CSerialPort::WriteToPort(char* string,int n)
    {
    	assert(m_hComm != 0);
    	memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
    	memcpy(m_szWriteBuffer, string, n);
    	m_nWriteSize = n;
    
    	// set event for write
    	SetEvent(m_hWriteEvent);
    }
    
    void CSerialPort::WriteToPort(LPCTSTR string)
    {
    	assert(m_hComm != 0);
    	memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
    	strcpy(m_szWriteBuffer, string);
    	m_nWriteSize=strlen(string);
    	// set event for write
    	SetEvent(m_hWriteEvent);
    }
    
    void CSerialPort::WriteToPort(BYTE* Buffer, int n)
    {
    	assert(m_hComm != 0);
    	memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
    	int i;
    	for(i=0; i<n; i++)
    	{
    		m_szWriteBuffer[i] = Buffer[i];
    	}
    	m_nWriteSize=n;
    
    	// set event for write
    	SetEvent(m_hWriteEvent);
    }
    
    
    void CSerialPort::SendData(LPCTSTR lpszData, const int nLength)
    {
    	assert(m_hComm != 0);
    	memset(m_szWriteBuffer, 0, nLength);
    	strcpy(m_szWriteBuffer, lpszData);
    	m_nWriteSize=nLength;
    	// set event for write
    	SetEvent(m_hWriteEvent);
    }
    
    BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize)
    {
    	//
    	//接收数据
    	//
    	assert(m_hComm!=0);
    	memset(lpszData,0,nSize);
    	DWORD mylen  = 0;
    	DWORD mylen2 = 0;
    	while (mylen<nSize) {
    		if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL)) 
    			return FALSE;
    		mylen += mylen2;
    
    		
    	}
    	
    	return TRUE;
    }

    VC串口修正类应用的小项目下载地址:http://download.csdn.net/detail/liquanhai/3763088

  • 相关阅读:
    bzoj2748:[HAOI2012]音量调节
    bzoj2287:[POJ Challenge]消失之物
    bzoj1485:[HNOI2009]有趣的数列
    Codeforces 620E New Year Tree
    CF813E Army Creation
    527D.Clique Problem
    4337: BJOI2015 树的同构
    Codeforces Round #443 (Div. 1) C. Tournament
    [BZOJ4913][SDOI2017]遗忘的集合
    [八省联考2018]林克卡特树lct
  • 原文地址:https://www.cnblogs.com/skyofbitbit/p/3650219.html
Copyright © 2011-2022 走看看