/*! \mainpage CnComm v1.51 multiple thread serial port communication library 
 *	\section About 
 *  
 *  \n Version: CnComm v1.51
 *  \n Usage: WINDOWS/WINCE multiple thread serial port communication library 
 *  \n Language: C++ (ANSI/UNICODE)
 *  \n Platform: WINDOWS(WIN98/NT/2000/XP/2003/Vista); WINCE 5.0 Emulator; Pocket PC 2003 Emulator; 
 *  \n Hardware: PC serial port; serial port server; USB port; virtual serial port;
 *  \n Compiler: BC++ 5(free tool); C++ BUILDER 4, 5, 6, X; EVC 4(sp4); G++ 3, 4; Intel C++ 7, 8, 9; VC++ 6(sp6), .NET, 2003, 2005;
 *  \n Author: llbird
 *  \n Email: wushaojian@21cn.com
 *  \n Blog: http://blog.csdn.net/wujian53 http://www.cppblog.com/llbird  
 *  \n Warranty: 2002.10 - 2009.8
 *
 *  \section Announce 
 *  \n 1) You are free to use/spread this code but leave related statement;                                           
 *  \n 2) Direct modification on this code is not recommended. To extent this code, C++ inheritance extension mechanism is required;                          
 *  \n 3) If you have modified this code directly and successfully, please send me a copy of modified version. We will share your experience in our website;                       *  \n 4) Versions under cnComm1.4 is not compatible. As this code has a huge change, it renames to cnComm;
 *  \n 5) All I ask is that you let me know so that if you have a real winner I can brag to my buddies that some of my code is in your app. I also wouldn't mind if you sent me a copy of your application since I like to play with new stuff.
 *  
 *  \section Log 
 *  \n 2009 v1.51 Modified version; it may be the last version as we have complete all the functions for serial port;
 *  \n 2009 v1.5  Add embedded block linked list buffer; Add the support of WINCE (Test with a simulator);
 *  \n 2008 v1.4  Add the support of multiple thread for I/O synchronization; Add the support of C++ expectation; rename to CnComm; Cn == C Next;
 *  \n 2007 v1.3  Details revise;
 *  \n 2006 v1.2  Details revise;
 *  \n 2005 v1.1  Details revise;
 *  \n 2004 v1.0  Adopt VC as naming style (Hungary style), have been tested on different WINDOW platform and compiler, first release cnComm;
 *  \n 2002 v0.1  Develop serial port communication basic class with traditional C++ inheritance mechanism and C naming style;
 */

#ifndef _CN_COMM_H_
#define _CN_COMM_H_

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <tchar.h>

#if defined(UNDER_CE) && !defined(CN_COMM_FOR_CE)
	#define CN_COMM_FOR_CE		UNDER_CE			//!< Configure WINCE support   
#endif

#ifndef CN_COMM_FOR_CE
	#include <mcx.h>
	#include <process.h>						// WINCE does not have process.h  
#endif

#ifndef ON_COM_MSG_BASE 
	#define ON_COM_MSG_BASE		WM_USER + 618		        //!< Base value of the message
#endif

// The receiving message WPARAM COM port number
#define ON_COM_RECEIVE			ON_COM_MSG_BASE + 0	//!< EV_RXCHAR 
#define ON_COM_RXCHAR			ON_COM_MSG_BASE + 0 //!< EV_RXCHAR 
#define ON_COM_CTS				ON_COM_MSG_BASE + 1 //!< EV_CTS  LPARAM==1 CTS ON 
#define ON_COM_DSR				ON_COM_MSG_BASE + 2 //!< EV_DSR  LPARAM==1 DSR ON
#define ON_COM_RING				ON_COM_MSG_BASE + 3 //!< EV_RING LPARAM==1 RING ON
#define ON_COM_RLSD				ON_COM_MSG_BASE + 4 //!< EV_RLSD LPARAM==1 RLSD ON
#define ON_COM_BREAK			ON_COM_MSG_BASE + 5 //!< EV_BREAK
#define ON_COM_TXEMPTY			ON_COM_MSG_BASE + 6 //!< EV_TXEMPTY 
#define ON_COM_ERROR			ON_COM_MSG_BASE + 7 //!< EV_ERR  LPARAM Error ID 
#define ON_COM_RXFLAG			ON_COM_MSG_BASE + 8 //!< EV_RXFLAG
#define ON_COM_POWER			ON_COM_MSG_BASE + 9 //!< EV_POWER 
#define ON_COM_EVENT1			ON_COM_MSG_BASE + 10//!< EV_EVENT1
#define ON_COM_EVENT2			ON_COM_MSG_BASE + 11//!< EV_EVENT2 
#define ON_COM_RX80FULL			ON_COM_MSG_BASE + 12//!< EV_RX80FULL
#define ON_COM_PERR				ON_COM_MSG_BASE + 13//!< EV_PERR

#ifndef CN_COMM_WAIT_EVENT
	#ifdef CN_COMM_FOR_CE
		#define CN_COMM_WAIT_EVENT	EV_RXCHAR | EV_ERR | EV_CTS | EV_DSR | EV_BREAK | EV_TXEMPTY | EV_RING | EV_RLSD | EV_POWER //!< WINCE default wait event| EV_RXFLAG 
	#else
		#define CN_COMM_WAIT_EVENT	EV_RXCHAR | EV_ERR | EV_CTS | EV_DSR | EV_BREAK | EV_TXEMPTY | EV_RING | EV_RLSD 	    //!< WIN32 default wait event| EV_RXFLAG
	#endif
#endif

#ifndef CN_COMM_BUFFER_MIN_BLOCK_SIZE
	#define CN_COMM_BUFFER_MIN_BLOCK_SIZE 1024		//!< define the minimum value of buffer block 
#endif

#if CN_COMM_BUFFER_MIN_BLOCK_SIZE < 4
	#error CN_COMM_BUFFER_MIN_BLOCK_SIZE must >= 4	//!< The minimum value of buffer block should not be lower than 4. 
#endif

#ifndef CN_ASSERT
	#define CN_2STR(L)		_T(#L)					//!< Convert expression L to a string 
	#define CN_LINE(L)		CN_2STR(L)				//!< Convert line number L to a string 
	/*! Internal assertion, enable exception will assert exception, or else the debug version will be exit, the release version can run normally without enabling exceptions */
	#define CN_ASSERT(E)	((E) ? true : CnComm::Assert(_T("CN_ASSERT(")_T(#E)_T(") failed; CnComm(")CN_LINE(__LINE__)_T("); ")))
#endif

//CN_COMM_STD_EXCEPTION CN_ASSERT Throw standard C++ expectation			
#ifdef CN_COMM_STD_EXCEPTION
	#include <stdexcept> //throw runtime_error(msg)
#endif

//CN_COMM_VCL_EXCEPTION CN_ASSERT Throw VCL expectation (C++ Builder)	
#if defined(CN_COMM_VCL_EXCEPTION) && defined(__BORLANDC__)
	#include <vcl.h> //throw new Exception(msg)	
#endif

//CN_COMM_MFC_EXCEPTION CN_ASSERT Throw MFC expection (VC++)			
#ifdef CN_COMM_MFC_EXCEPTION
	#include <Afx.h> //throw new MfcException(msg)	
#endif

/*! \class CnComm 
	\version 1.5 
	\date 2002.10-2009.4
	\author llbird(wushaojian@21cn.com http://www.cppblog.com/llbird  http://blog.csdn.net/wujian53) 
	\brief WIN32/WINCE C++ (ANSI/UNICODE) multiple thread serial port communication basic library  
	\example doc_0.cpp Example 0 \example doc_1.cpp \example doc_2.cpp \example doc_3.cpp \example SerialDlg.cpp
 */
class CnComm    
{
private:

    CString cstrHex;  // Store all the data have transmitted into Hex character string
    CString cstrRet; // Return
    bool bHexRcEn;  // Allow storing

    void HexSave(char *szStr, DWORD dwSize) 
    {
        if (bHexRcEn) {
            char *p;
            int h;
            int l;
            p = cstrHex.GetBuffer(cstrHex.GetLength() + dwSize * 3 + 1);	// Two Hex. bits + space 
            p += cstrHex.GetLength(); 

            for (int i = 0; i < dwSize; i++ ) {
                h = ((unsigned int)szStr[i] >> 4) & 0x0F;
                l = ((unsigned int)szStr[i] & 0x0F);
                *p++ = (h > 9) ? (h - 10 + 'A') : (h + '0');
                *p++ = (l > 9) ? (l - 10 + 'A') : (l + '0');
                *p++ = ' ';
            }
            *p++ = '\0';

            cstrHex.ReleaseBuffer();
        }
    }

public:
    void EnCommHexSave(bool en)
    {
        bHexRcEn = en;
    }

    void CommHexFlush()
    {
        cstrHex = "";
    }

    CString GetCommHex()
    {
        cstrRet = cstrHex;
        cstrHex = "";
        return cstrRet;
    }

public:
	//! Critical region
	struct	InnerLock;
	//! Buffer class
	class	BlockBuffer;	
	//! MFC expectation
	class	MfcException;				
	//!  enum value for mode setting. It is a 32 bit mask code.
	enum OptionEnum
	{
		EN_THREAD	= 0x00000001,	//!< Enable Watch Thread, start WatchThread after opening of serial port  
		EN_OVERLAPPED	= 0x00000002,	//!< Enable asyhnchronous I/O overlapped mode
		EN_RX_BUFFER	= 0x00000004,	//!< Enable Read buffer
		EN_TX_BUFFER	= 0x00000008,	//!< Enable Write buffer
		EN_RX_THREAD	= 0x00000010,	//!< Enable Read thread, ReadThead not used yet
		EN_TX_THREAD	= 0x00000020,	//!< Enable Write thread,used for WINCE duplex operations, user should enable write buffer, and enable WriteThread after opening the serial port
		EN_SUSPEND		= 0x00000040,	//!< Enable thread has 1 level higher priority
		EN_ABOVE_NORMAL	= 0x00000080,	//!< Enable thread has 1 level higher priority 
		EN_FLUSH	= 0x00000100,	//!< When serial port is close, output port will output any remaining data (within the terminal buffer), and block waiting                        
		EN_FLUSH_ALL	= 0x00000200	//!< Same with above (including write buffer and terminal queue), if you reload the wirte module, but the writing is not completeed yet, then the thread will be suspended and caonot be close normally.
	};
	//! Constructor. function for application mode configuration  \param[in] dwOption It is combined by OptionEnum as required.
#ifdef CN_COMM_FOR_CE
	//! WINCE: When serial port is opened, WatchThread will be enabled by default. Enable write independent thread, and enable Write buffer.
	CnComm(DWORD dwOption = EN_THREAD ) 
#else 
	//! WIN32: When serial port is opened, WatchThread will be enabled by default. And enable asynchronism I/O overlapped mode.
	CnComm(DWORD dwOption = EN_THREAD | EN_OVERLAPPED)
#endif
	{
		bHexRcEn = false; 
		Init(); 
		SetOption(dwOption);
	}
	//! Constructor in another mode, compatible with cnComm1~1.3 \param[in] bThread Enable monitor thread \param[in] bOverlapped enable I/O overlapped mode
	CnComm(bool bThread, bool bOverlapped)
	{
		DWORD dwOption = 0;

		if (bThread)
			dwOption |= EN_THREAD;

		if (bOverlapped)
			dwOption |= EN_OVERLAPPED;

		Init(); 
		SetOption(dwOption);
	}
	//! Destructor, close serial port automaticaly 
	virtual ~CnComm()
	{
		Close(); 
		Destroy();
	}
	//! Check whether the serial port is on.
	bool IsOpen()
	{
		return hComm_ != INVALID_HANDLE_VALUE;
	}
	//! Check whether the serial port is on.
	operator bool ()
	{
		return hComm_ != INVALID_HANDLE_VALUE;
	}
	//! Get the handle of serial port
	HANDLE GetHandle()
	{
		return hComm_;
	}
	//! Get the handle of serial port
	operator HANDLE()
	{
		return hComm_;
	}
	//! Get the number of serial port
	DWORD GetPort()
	{
		return dwPort_;
	}
	//! Get the full name of serial port
	LPCTSTR GetPortName()
	{
		return szName_;
	}
	//! Get the basic configuration parameters for CnComm. Return 32 bits configuration mask code.
	DWORD GetOption()
	{
		return dwOption_;
	}
	//! Set the basic configuration parameters of CnComm, only valid before opening the serial port, \param[in] dwOption: 32 bit configuraion mask code 
	void SetOption(DWORD dwOption)
	{
		CN_ASSERT(!IsOpen());//! When the serial port is on, this parameter is not configurable. 

		dwOption_ = dwOption;

	#ifdef CN_COMM_FOR_CE
		CN_ASSERT(!IsOverlappedMode()); //! Overlap I/O EN_OVERLAPPED mask code is not allowed in WINCE. 
		dwOption_ &= (~EN_OVERLAPPED);
	#endif
	}
	//! Modify the basic configuration parameter CnComm,only valid before opening the serial port; \param[in] dwRemove: 32-bit configuration mask code to delete; \param[in] dwAdd: 32-bit configuraiton mask code   
	void ModifyOption(DWORD dwRemove, DWORD dwAdd)
	{
		CN_ASSERT(!IsOpen());//! When the serial port is on, this parameter is not configurable. 

		dwOption_ &= ~dwRemove;
		dwOption_ |= dwAdd;

	#ifdef CN_COMM_FOR_CE
		CN_ASSERT(!IsOverlappedMode()); //! Overlapped I/O EN_OVERLAPPED mask code is forbidden in WINCE.
		dwOption_ &= (~EN_OVERLAPPED);
	#endif
	}
	//! Check whether is overlapped IO mode
	bool IsOverlappedMode()
	{
		return dwOption_ & EN_OVERLAPPED ? true : false;
	}
	//! Check whether is output buffer mode
	bool IsTxBufferMode()
	{
		return dwOption_ & EN_TX_BUFFER ? true : false;
	}
	//! Check whether is input buffer mode
	bool IsRxBufferMode()
	{
		return dwOption_ & EN_RX_BUFFER ? true : false;
	}
	//! Link the window handle of message
	void SetWnd(HWND hWnd)
	{
		CN_ASSERT(::IsWindow(hWnd));
		hNotifyWnd_ = hWnd;
	}
	//! Link the window handle of message
	HWND GetWnd()
	{
		return hNotifyWnd_;
	}
	//! Link the window handle of message 
	void SetNotifyThreadId(DWORD dwId)
	{
		hNotifyThreadId_ = dwId;
	}
	//! Link the window handle of message 
	DWORD GetNotifyThreadId()
	{
		return hNotifyThreadId_;
	}
	//! Set the transmission notice, the receiving character is set to a minimum value. 
	void SetNotifyNum(DWORD dwNum)
	{
		dwNotifyNum_ = dwNum;
	}
	//! Get thread handle; return thread handle in \param[in] iOption, if EN_THREAD is ture, get hWatchThread_ thread handle, if EN_RX_THREAD is true, get hReadThread_ thread handle, if EN_TX_THREAD is true, get hWriteThread_ thread handle; \return value: HANDLE, the handle of the thread 
	HANDLE GetThread(int iOption = EN_THREAD)
	{
		return iOption == EN_THREAD ? hWatchThread_ : ((iOption == EN_RX_THREAD) ? hReadThread_ : hWriteThread_);
	}
	//! Set events to watch, only valid before open 
	void SetWaitEvent(DWORD dwEvent = CN_COMM_WAIT_EVENT)
	{
		CN_ASSERT(!IsOpen());/// Cannot set parameters under open state  

		dwWaitEvent_ = dwEvent;
	}
	//! Input buffer
	BlockBuffer& Input()
	{
		return I_;
	}
	//! Output buffer
	BlockBuffer& Output()
	{
		return O_;
	}
	//! Get counting number for input or output; \param[in], if bInput is true, get input counts, or else get output counts, default value is true; under overlapped IO mode, the count number of output is not precise, because in this mode output will be done by system in background mode, and it assumes all data is fully output   
	DWORD GetCounter(bool bInput = true)
	{
		return bInput ? dwInCount_ : dwOutCount_;
	}
	//! Clear input/output counter
	void ResetCounter()
	{
		dwInCount_ = dwOutCount_ = 0;
	}
	//! Open serial port, please notice the difference with cnComm1~1.3; where cnComm1~1.3 will configure the port with 9600, n, 8, 1 values, but for 1.5, it only opens the port without configuring the parameters like Baud rates; \param[in] dwPort: the serial port serial number, range: 1~1024
	bool Open(DWORD dwPort)
	{
		if (!CN_ASSERT(dwPort>=1 && dwPort<=1024))
			return false;
		
		BindPort(dwPort);
		
		if(!CN_ASSERT(OpenPort()))
			return false;
		
		if(!CN_ASSERT(SetupPort()))
			return Close(), false;

		if ((dwOption_ & (EN_THREAD|EN_RX_THREAD|EN_TX_THREAD)) && !CN_ASSERT(BeginThread()))
			return Close(), false;

		return true;
	}
	/*! \param[in] dwPort: Serial port serial number, range: 1~1024; \param[in] dwBaudRate: Baud rate \param[in] btParity: Parity check; \param[in] btByteSize: number of data bits; \param[in] btStopBits: Number of stop bits*/
	//! Open serial port with default parameter: 9600, n, 8, 1 \sa bool Open(DWORD dwPort, LPCTSTR szSetStr) 
	bool Open(DWORD dwPort, DWORD dwBaudRate, BYTE btParity = NOPARITY, BYTE btByteSize = 8, BYTE btStopBits = ONESTOPBIT)
	{
		if (!CN_ASSERT(dwPort>=1 && dwPort<=1024))
			return false;
		
		BindPort(dwPort);
		
		if (!CN_ASSERT(OpenPort()))
			return false;
		
		if (!CN_ASSERT(SetState(dwBaudRate, btParity, btByteSize, btStopBits)))
			return Close(), false;

		if (!CN_ASSERT(SetupPort()))
			return Close(), false;

		if ((dwOption_ & (EN_THREAD|EN_RX_THREAD|EN_TX_THREAD)) && !CN_ASSERT(BeginThread()))
			return Close(), false;

		return true;
	}
#ifndef CN_COMM_FOR_CE
	/*! \param[in] dwPort: Serial port serial number, range: 1~1024 \param[in] szSetStr: Parameter string: "BBBB,P,D,S"  Example: "9600,N,8,1", "1200,O,7,2"   
         BBBB is the Baud rate, P is the Parity check (E | M | N | O | S) D is Number of data bits (4 ~ 8), and S is the number of stop bits (1 | 1.5 | 2)  
         \code Open(1, "9600,E,8,2"); \endcode  \b Please notice the sequence of the parameter string, and check the return value  
         Does not support WINCE! I've written a substitute function, but considering the complexity of WINCE hardware, the porting of it may be difficult, so I removed this substitude function in this version. */  
    //! Open serial port, and use a character string to configure it.
	bool Open(DWORD dwPort, LPCTSTR szSetStr)
	{
		if (!CN_ASSERT(dwPort>=1 && dwPort<=1024))
			return false;
		
		BindPort(dwPort);
		
		if (!CN_ASSERT(OpenPort()))
			return false;
		
		if (!CN_ASSERT(SetState(szSetStr))) 
			return Close(), false;

		if (!CN_ASSERT(SetupPort()))
			return Close(), false;

		if ((dwOption_ & (EN_THREAD|EN_RX_THREAD|EN_TX_THREAD)) && !CN_ASSERT(BeginThread()))
			return Close(), false;
		
		return true;
	}
#endif
	/*! \param[in] dwPort This parameter means nothing, it only identifies the port numbers, notice that it should not be the same with other port numbers  
        \param[in] szPortName: Specified port name, just like \\\\.\\COM1 under Windows or COM1 under WINCE: Some virtual serial port could have special name 
        \param[in] dwBaudRate: Baud rate; \param[in] btParity: Parity check; \param[in] btByteSize: Number of data bits; \param[in] btStopBits: number of stop bits.  
        \code Open(9999, "COM3:", 2400); \endcode   \code Open(1028, "COM3:", 9600, N, 7, ONESTOPBIT); \endcode */  
    //! Use a specified port name to open a serial port. 
	bool Open(DWORD dwPort, LPCTSTR szPortName, DWORD dwBaudRate, BYTE btParity = NOPARITY, BYTE btByteSize = 8, BYTE btStopBits = ONESTOPBIT)
	{
		if(!CN_ASSERT(_tcslen(szPortName) < 64 - 1))
			return false;
		
		_tcscpy(szName_, szPortName);
		dwPort_ = dwPort;// used for notifing message
		
		if (!CN_ASSERT(OpenPort()))
			return false;
		
		if (!CN_ASSERT(SetState(dwBaudRate, btParity, btByteSize, btStopBits)))
			return Close(), false;

		if (!CN_ASSERT(SetupPort()))
			return Close(), false;

		if ((dwOption_ & (EN_THREAD|EN_RX_THREAD|EN_TX_THREAD)) && !CN_ASSERT(BeginThread()))
			return Close(), false;
		
		return true;
	}
	//! Read physical port directly \param[out] pBuffer: target buffer; \param[in] dwLength: Length to read; \param[in] dwWaitTime: Wait time (default: INFINITE) \return value: returns the actual number of bytes read out  
	DWORD ReadPort(LPVOID pBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
	{
		COMSTAT Stat;
		DWORD	dwError, dwReadResult = 0;
		
		if(!CN_ASSERT(IsOpen()) || !dwLength)
			return 0;
		
		if(::ClearCommError(hComm_, &dwError, &Stat) && dwError > 0)
			::PurgeComm(hComm_, PURGE_RXABORT);

	#ifndef CN_COMM_FOR_CE
		if (IsOverlappedMode())//! Under overlapped I/O mode, dwWaitTime==INFINITE will wait until the task complete or overtime, the timeout time depends on the timeout structure, the default setting is 0.5 second   
        {   
            if (dwWaitTime == INFINITE)//! Direct read from the ports without using a buffer will cause data block, this condition is similar to the 1.3 version Read(pBuffer, dwLength, false);   
			{
				if (!::ReadFile(hComm_, pBuffer, dwLength, &dwReadResult, &RO_))
				{//! Using CnComm within the interface, it is recommended to use RX buffer or specified timeout time in dwWaitTime parameter
					if (CN_ASSERT(::GetLastError() == ERROR_IO_PENDING))
					{
						while (!CN_ASSERT(::GetOverlappedResult(hComm_, &RO_, &dwReadResult, TRUE)))
							;
					}
				}
			}
			else//! Under overlapped I/O mode, dwWaitTime==INFINITE will wait until the task complete or overtime, the timeout time depends on the timeout structure                         
			{   //! dwWaitTime = 0 has the same function with the old 1.3 version 
				DWORD dwBegin = GetTickCount(), dwEnd, dwCost, uReadLength, uReadReturn;
				
				uReadLength = Stat.cbInQue > dwLength ? dwLength : Stat.cbInQue;
				CN_ASSERT(::ReadFile(hComm_, pBuffer, uReadLength, &uReadReturn, &RO_));
				dwReadResult += uReadReturn;

				do
				{
					if (!::ReadFile(hComm_, (LPBYTE)pBuffer + dwReadResult, 1, &uReadReturn, &RO_))
					{
						if (dwWaitTime > 5 && WaitForSingleObject(RO_.hEvent, dwWaitTime) == WAIT_OBJECT_0)
						{
							dwEnd = GetTickCount();
							dwCost = dwEnd>=dwBegin ? dwEnd-dwBegin : DWORD(-1L)-dwBegin+dwEnd;
							CN_ASSERT(::GetOverlappedResult(hComm_, &RO_, &uReadReturn, FALSE));
							dwWaitTime = dwWaitTime > dwCost ? dwWaitTime-dwCost : 0;
						} 
						else
						{
							CN_ASSERT(::PurgeComm(hComm_, PURGE_RXABORT));
							break;
						}
					}
				}
				while (uReadReturn && ++dwReadResult < dwLength);
			}
			return dwInCount_ += dwReadResult, dwReadResult;
		}
	#endif
		//! Under Block I/O and WinCE I/O mode, dwWaitTime is not valid, the timeout is determined by the timeout structure, the default setting is 1/4 second
		CN_ASSERT(::ReadFile(hComm_, pBuffer, dwLength, &dwReadResult, NULL));
		return dwInCount_ += dwReadResult, dwReadResult;
	}
	//! Read in dwLength characters from the serial port and save to pBuffer, return the number of characters actually read, it can read in any data 
	DWORD Read(LPVOID pBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
	{
		CN_ASSERT(pBuffer);

		if (dwOption_ & EN_RX_BUFFER)
		{
			BlockBuffer::InnerLock locker(&I_);
			return I_.Read(pBuffer, dwLength);
		}

	#ifdef CN_COMM_FOR_CE
		return ReadPort(pBuffer, dwLength, dwWaitTime);
	#else
		return ReadPort(pBuffer, dwLength, dwWaitTime);
	#endif
	}
	//! Read in dwLength-1 ANSI characters from serail port and save to szBuffer, return C mode pointer to string, suitable for normal text communications.
	char * ReadString(char *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
	{
		CN_ASSERT(szBuffer);
		szBuffer[Read(szBuffer, dwLength - 1, dwWaitTime)] = '\0';
		return szBuffer;
	}
	//! Read UNICODE characters in a number of dwLength-1 from seiral port to szBuffer. Return C mode character string seiral pointer. It is applicable for normal character communication.
	wchar_t * ReadString(wchar_t *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
	{
		CN_ASSERT(szBuffer);
		szBuffer[(Read(szBuffer, (dwLength - 1)*sizeof(wchar_t), dwWaitTime) +1)/ sizeof(wchar_t)] = L'\0';
		return szBuffer;
	}
	//! Write port directly
	DWORD WritePort(LPCVOID pBuffer, DWORD dwLength)
	{
	//	if(!CN_ASSERT(IsOpen()) || !dwLength)
	//		return 0;
		
		if(!IsOpen() || !dwLength)
			return 0;

		DWORD dwError;
		unsigned long uWriteLength = 0;

		if(::ClearCommError(hComm_, &dwError, NULL) && dwError > 0) 
			::PurgeComm(hComm_, PURGE_TXABORT); 

	#ifndef CN_COMM_FOR_CE
		if (IsOverlappedMode())//! For overlapped I/O, it returns 0, if the write counter is added with the written size, it assumes the write operation is success. 
		{//! For overlapped I/O, timeout will be controlled by the timeout structure, default value is length*1ms+10 secondc. 
			if(!::WriteFile(hComm_, pBuffer, dwLength, &uWriteLength, &WO_) 
				&& !CN_ASSERT(::GetLastError() == ERROR_IO_PENDING))
				uWriteLength = 0;
			else
				dwOutCount_ += dwLength;
		}
		else
	#endif //! For blocked I/O or WINCE, returns the actual written length, the actual written number of data will be added to the write counter directly, default timeout is 1/4 second 
			::WriteFile(hComm_, pBuffer, dwLength, &uWriteLength, NULL), dwOutCount_ += uWriteLength;

		return uWriteLength;
	}
	//! Write EN_TX_BUFFER to determine data is written to the buffer or written to the port directly.
	DWORD Write(LPCVOID pBuffer, DWORD dwLength)
	{
		HexSave((char *)pBuffer, dwLength);

		if (dwLength && (dwOption_&EN_TX_BUFFER))
		{
			BlockBuffer::InnerLock locker(&O_);

			O_.Write(pBuffer, dwLength);

			if (dwOption_ & EN_TX_THREAD)
				SetEvent(hWriteEvent_);
			else
			{
			#ifdef CN_COMM_FOR_CE
				SetCommMask(hComm_, dwWaitEvent_);
			#else
				if (IsOverlappedMode())
					SetCommMask(hComm_, dwWaitEvent_);
				else
					SetEvent(hWatchEvent_);
			#endif
			}
			return 0;
		}
			
	#ifdef CN_COMM_FOR_CE
		return WritePort(pBuffer, dwLength);
	#else
		return WritePort(pBuffer, dwLength);
	#endif
	}
	//! Write ANSI charcter to serial port, write with an ANSI C mode string pointer  
	DWORD Write(const char *szBuffer)
	{
		CN_ASSERT(szBuffer);
		
		return Write((LPCVOID)szBuffer, strlen(szBuffer));
	}
	//! Write UNICODE charcter to serial port, write with an ANSI C mode string pointer  
	DWORD Write(const wchar_t *szBuffer)
	{
		CN_ASSERT(szBuffer);
		
		return Write((LPCVOID)szBuffer, wcslen(szBuffer)*sizeof(wchar_t));
	}
	//! Write data to serial port, data stored in szBuffer can output string with formats, including buffer length
	DWORD Write(char *szBuffer, DWORD dwLength, char * szFormat, ...)
	{
		va_list va;
		va_start(va, szFormat);
		_vsnprintf(szBuffer, dwLength, szFormat, va);
		va_end(va);
		
		return Write(szBuffer);
	}
	//! Write UNICODE data to serial port, data stored in szBuffer can output string with formats, including buffer length
	DWORD Write(wchar_t *szBuffer, DWORD dwLength, wchar_t * szFormat, ...)
	{
		va_list va;
		va_start(va, szFormat);
		_vsnwprintf(szBuffer, dwLength, szFormat, va);
		va_end(va);
		
		return Write(szBuffer);
	}
	//! Write data to serial port, data stored in szBuffer can output string with formats, it does not check buffer length, so beware of overflow condition
	DWORD Write(char *szBuffer, char * szFormat, ...)
	{
		va_list va;
		va_start(va, szFormat);
		vsprintf(szBuffer, szFormat, va);
		va_end(va);
		
		return Write(szBuffer);
	}
	//! Write data to serial port, data stored in szBuffer can output string with formats, it does not check buffer length, so beware of overflow condition
	DWORD Write(wchar_t *szBuffer, wchar_t * szFormat, ...)
	{
		va_list va;
		va_start(va, szFormat);
		vswprintf(szBuffer, szFormat, va);
		va_end(va);
		
		return Write(szBuffer);
	}
	//! Force output data within the queue and wait  
	void FlushPort()
	{
		if(CN_ASSERT(IsOpen()))
			FlushFileBuffers(hComm_);
	}
	//! Force output data within the queue and wait  
	void Flush()
	{
		if(CN_ASSERT(IsOpen()))
		{
			if (dwOption_ & EN_TX_BUFFER)
			{
				while(O_.SafeSize())
					Sleep(50);
			}

			FlushFileBuffers(hComm_);
		}
	}
	//! Preempt output character 
	bool TransmitChar(char c)
	{
		if (CN_ASSERT(IsOpen()))
		{
			if (::TransmitCommChar(hComm_, c))
				return dwOutCount_++, true;
		}

		return false;
	}
	//! Enable auxiliary thread control 
	bool BeginThread(DWORD dwThreadOption = 0) 
	{
		DWORD dwCreationFlags;

		if(CN_ASSERT(!hWatchThread_ && !hReadThread_ && !hWriteThread_)) 
		{//! This thread can be enabled only when no other thread is running.
			bContinue_		= true;
			dwCreationFlags	= dwOption_ & EN_SUSPEND ? CREATE_SUSPENDED : 0;

			if (dwThreadOption)//! Creat monitor thread, independent read thread or write thread according to the configuration
				dwOption_ |= dwThreadOption;
			
		#if defined(_MT) && !defined(CN_COMM_FOR_CE)
			unsigned int id, rid, wid;

			if (dwOption_ & EN_THREAD)
				hWatchThread_ = (HANDLE)_beginthreadex(NULL, 0, WatchThreadProc, this, dwCreationFlags, &id);

			if (dwOption_ & EN_RX_THREAD)
				hReadThread_ = (HANDLE)_beginthreadex(NULL, 0, ReadThreadProc, this, dwCreationFlags, &rid);

			if (dwOption_ & EN_TX_THREAD)
				hWriteThread_ = (HANDLE)_beginthreadex(NULL, 0, WriteThreadProc, this, dwCreationFlags, &wid);
		#else
			DWORD id, rid, wid;

			if (dwOption_ & EN_THREAD)
				hWatchThread_ = ::CreateThread(NULL, 0, WatchThreadProc, this, dwCreationFlags , &id); 

			if (dwOption_ & EN_RX_THREAD)
				hReadThread_ = ::CreateThread(NULL, 0, ReadThreadProc, this, dwCreationFlags, &rid); 

			if (dwOption_ & EN_TX_THREAD)
				hWriteThread_ = ::CreateThread(NULL, 0, WriteThreadProc, this, dwCreationFlags, &wid); 
		#endif

			if (dwOption_ & EN_THREAD)
			{
				CN_ASSERT(hWatchThread_ != NULL);
				hWatchThreadId_ = id;

				if (!hWatchThread_)
				{
					EndThread();
					return false;
				}
				else
				{
					if (dwOption_ & EN_ABOVE_NORMAL)
						SetThreadPriority(hWatchThread_, THREAD_PRIORITY_ABOVE_NORMAL);
				}
			}

			if (dwOption_ & EN_RX_THREAD)
			{
				CN_ASSERT(hReadThread_ != NULL);
				hReadThreadId_	= rid;

				if (!hReadThreadId_)
				{
					EndThread();
					return false;
				}
				else
				{
					if (dwOption_ & EN_ABOVE_NORMAL)
						SetThreadPriority(hReadThread_, THREAD_PRIORITY_ABOVE_NORMAL);
				}
			}

			if (dwOption_ & EN_TX_THREAD)
			{
				CN_ASSERT(hWriteThread_ != NULL);
				hWriteThreadId_	= wid;

				if (!hWriteThreadId_)
				{
					EndThread();
					return false;
				}
				else
				{
					if (dwOption_ & EN_ABOVE_NORMAL)
						SetThreadPriority(hWriteThread_, THREAD_PRIORITY_ABOVE_NORMAL);
				}
			}

			return true; 
		}

		return false;
	}
	//! Suspend thread
	bool SuspendThread(int iOption = EN_THREAD)
	{
		return ::SuspendThread(GetThread(iOption)) != 0xFFFFFFFF;
	}
	//! Resume from suspend 
	bool ResumeThread(int iOption = EN_THREAD)
	{
		return ::ResumeThread(GetThread(iOption)) != 0xFFFFFFFF;
	}
	//! End thread
	bool EndThread(DWORD dwWaitTime = 500)
	{
		if(hWatchThread_ || hReadThread_ || hWriteThread_) 
		{
			if ((dwOption_&EN_FLUSH_ALL) && (dwOption_&EN_TX_BUFFER))
			{//! If EN_FLUSH_ALL is enabled, wait until write buffer is clear. If write thread is work improperly, it will be suspended 
				while(O_.Size())
					Sleep(50);
			}

			bContinue_ = false;

		#ifdef CN_COMM_FOR_CE
			::SetCommMask(hComm_, 0);
		#else
			if (IsOverlappedMode())
				::SetCommMask(hComm_, 0);
		#endif

			if (hWatchThread_)
			{
				SetEvent(hWatchEvent_);
				if(::WaitForSingleObject(hWatchThread_, dwWaitTime) != WAIT_OBJECT_0)
					if(!::TerminateThread(hWatchThread_, 0))
						return false;

				::CloseHandle(hWatchThread_);
				hWatchThread_ = NULL;
			}

			if (hReadThread_)
			{
				SetEvent(hReadEvent_);
				if(::WaitForSingleObject(hReadThread_, dwWaitTime) != WAIT_OBJECT_0)
					if(!::TerminateThread(hReadThread_, 0))
						return false;

				::CloseHandle(hReadThread_);
				hReadThread_ = NULL;
			}
			
			if (hWriteThread_)
			{
				SetEvent(hWriteEvent_);
				if(::WaitForSingleObject(hWriteThread_, dwWaitTime) != WAIT_OBJECT_0)
					if(!::TerminateThread(hWriteThread_, 0))
						return false;

				::CloseHandle(hWriteThread_);
				hWriteThread_ = NULL;
			}
				
			return true;
		}

		return false;
	}
	//! Close the serial port and end related thread
	virtual void Close(DWORD dwWaitTime = 500)
	{
		if(IsOpen())  
		{
			EndThread(dwWaitTime);//! End thread synchronously

			if (dwOption_&EN_FLUSH || dwOption_&EN_FLUSH_ALL)
				FlushFileBuffers(hComm_);			

			::PurgeComm(hComm_, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_RXABORT); 
			::CloseHandle(hComm_);
			hComm_ = INVALID_HANDLE_VALUE;
		}
	}
	//! Lock
	void Lock()							
	{	
		::EnterCriticalSection(&CS_);		
	}
	//! Unlock
	void Unlock()						
	{	
		::LeaveCriticalSection(&CS_);		
	}
	//! Autolock, used in the function. Lock and unlock procedure is performed by using the lift cycle of the object
	struct InnerLock
	{
		CnComm* ptr;//!< CnComm  Object pointer
		//! Lock
		InnerLock(CnComm* p) : ptr(p)	
		{	
			ptr->Lock();						
		}
		//! Unlock
		~InnerLock()					
		{	
			ptr->Unlock();						
		}
	};
	//! Get the serial port parameter DCB
	DCB *GetState(DCB *pDcb = NULL)
	{
		return CN_ASSERT(IsOpen()) && ::GetCommState(hComm_, pDcb == NULL ? &DCB_ : pDcb) == TRUE ? (pDcb == NULL ? &DCB_ : pDcb) : NULL;
	}
	//! Set serial port parameter DCB
	bool SetState(DCB *pDcb = NULL)
	{
		return CN_ASSERT(IsOpen()) ? ::SetCommState(hComm_, pDcb == NULL ? &DCB_ : pDcb) == TRUE : false;
	}
	//! Set serial port parameter: Baud rate, stop bit and so on ***
	bool SetState(DWORD dwBaudRate, BYTE btParity = NOPARITY, BYTE btByteSize = 8, BYTE btStopBits = ONESTOPBIT)
	{ 
		if(CN_ASSERT(IsOpen()))
		{
			if(::GetCommState(hComm_, &DCB_) != TRUE)
				return false;

			DCB_.BaudRate = dwBaudRate;
			DCB_.ByteSize = btByteSize;
			DCB_.Parity   = btParity;
			DCB_.StopBits = btStopBits;
			DCB_.fParity  = (btParity != NOPARITY);

			return ::SetCommState(hComm_, &DCB_) == TRUE;
		}
		return false;
	}
#ifndef CN_COMM_FOR_CE
	//! Set serial port parameter. Support configuration with character string "9600, n, 8, 1"
	bool SetState(LPCTSTR szSetStr) 
	{
		if(CN_ASSERT(IsOpen()))
		{
			if(!::GetCommState(hComm_, &DCB_))
				return false;

			if(!BuildCommDCB(szSetStr, &DCB_))
				return false;

			DCB_.fParity  = (DCB_.Parity != NOPARITY);
			return ::SetCommState(hComm_, &DCB_) == TRUE;
		}

		return false;
	}
#endif
	//! Get timeout structure
	LPCOMMTIMEOUTS GetTimeouts(LPCOMMTIMEOUTS lpCO = NULL)
	{
		return CN_ASSERT(IsOpen()) && ::GetCommTimeouts(hComm_, lpCO ? lpCO : &CO_) == TRUE  ? (lpCO ? lpCO : &CO_) : NULL;
	}
	//! Set timeout time
	bool SetTimeouts(LPCOMMTIMEOUTS lpCO = NULL)
	{
		return CN_ASSERT(IsOpen()) ? ::SetCommTimeouts(hComm_, lpCO ? lpCO : &CO_) == TRUE : false;
	}
	//! Set the I/O buffer size of serial port
	bool Setup(DWORD dwInputSize = 4096, DWORD dwOutputSize = 4096)
	{
		return CN_ASSERT(IsOpen()) ? ::SetupComm(hComm_, dwInputSize, dwOutputSize) == TRUE : false; 
	}
	//! Adjust port functions
	bool Escape(DWORD dwType)
	{
		return CN_ASSERT(IsOpen()) ? EscapeCommFunction(hComm_, dwType) != 0 : false;
	}
	//! Get the related signal states of modem
	DWORD GetModemStatus()
	{
		DWORD dwModemStat = 0;
		return CN_ASSERT(IsOpen()) && GetCommModemStatus(hComm_, &dwModemStat) ? dwModemStat : 0;
	}
	//! Get port parameters \param[in] pCP: structure pointer, if pCP==NULL, CnComm will be assigned from memory, and released by CnComm, user don't need to release it himself.
	LPCOMMPROP GetProperties(LPCOMMPROP pCP = NULL)	
	{
		if (CN_ASSERT(IsOpen()))
		{
			if (!pCP)
			{
			#ifdef CN_COMM_FOR_CE
				USHORT dwSize = sizeof(COMMPROP);
			#else
				USHORT dwSize = sizeof(COMMPROP) + sizeof(MODEMDEVCAPS);
			#endif

				if (!pCP_)
					pCP_ = (LPCOMMPROP) new BYTE[dwSize];

				if (pCP_)
				{
					memset(pCP_, 0, dwSize);

					pCP_->wPacketLength = dwSize;
				#ifndef CN_COMM_FOR_CE
					pCP_->dwProvSubType = PST_MODEM;
				#endif
					pCP_->dwProvSpec1	= COMMPROP_INITIALIZED;
					pCP = pCP_;
				}
			}
		}

		return pCP && GetCommProperties(hComm_, pCP) ? pCP : NULL;
	}
	//! Get event flag
	DWORD GetMask()
	{
		DWORD dwMask;
		return CN_ASSERT(IsOpen()) && GetCommMask(hComm_, &dwMask) ? dwMask : 0;	
	}
	//! Clear port buffer
	bool Purge(DWORD dwPara = PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR)
	{
		return CN_ASSERT(IsOpen()) ? ::PurgeComm(hComm_, dwPara)==TRUE : false;
	}
	//! Get error code
	DWORD ClearError()
	{
		DWORD dwError;
		return CN_ASSERT(IsOpen()) && ::ClearCommError(hComm_, &dwError, NULL) ? dwError : 0;
	}
	//! Get the character number of read physical buffer
	DWORD GetQueueCount(bool bInput = true)
	{
		COMSTAT Stat;
		DWORD dwError;
		
		return CN_ASSERT(IsOpen()) && ::ClearCommError(hComm_, &dwError, &Stat) ? (bInput ? Stat.cbInQue : Stat.cbOutQue) : (DWORD)-1L;
	}
	//! Modem CTS ON
	bool CheckCTS()				
	{	
		return (GetModemStatus()&MS_CTS_ON) != 0;		
	}
	//! Modem DSR ON
	bool CheckDSR()				
	{	
		return (GetModemStatus()&MS_DSR_ON) != 0;		
	}
	//! Modem Ring ON
	bool CheckRING()				
	{	
		return (GetModemStatus()&MS_RING_ON) != 0;		
	}
	//! Modem RLSD ON
	bool CheckRLSD()				
	{	
		return (GetModemStatus()&MS_RLSD_ON) != 0;		
	}
	//! DTR level control
	bool SetDTR(bool bSetOrClr = true)	
	{	
		return Escape(bSetOrClr ? SETDTR : CLRDTR);		
	}
	//! RTS level control
	bool SetRTS(bool bSetOrClr = true)	
	{	
		return Escape(bSetOrClr ? SETRTS : CLRRTS);		
	}
	//! Modem
	bool SetBreak(bool bSetOrClr = true)	
	{	
		return Escape(bSetOrClr ? SETBREAK : CLRBREAK);	
	}
#ifdef CN_COMM_FOR_CE
	//! WINCE
	bool SetIR(bool bSetOrClr)	
	{	
		return Escape(bSetOrClr ? SETIR : CLRIR);	
	}
#endif
	//! Flow control SETXON SETXOFF
	bool SetX(bool bOnOrOff)				
	{	
		return Escape(bOnOrOff ? SETXON : SETXOFF);							
	}
	//! Buffer class based on the communication features, it is a single linked list memory block with some extension to support API 
	class BlockBuffer
	{
	public:
		//! Buffer memory block 
		struct Block
		{
			DWORD			B_;		//!< Starting offset
			DWORD			E_;		//!< Ending offset
			DWORD			S_;		//!< Size of block, the maximum size is unlimited, the minimum size is determined by                                                                      CN_COMM_BUFFER_MIN_BLOCK_SIZE
			Block*			N_;		//!< Next block pointer 
			BYTE			P_[4];	//!< Buffer pointer, its size is dependent on parameter S_ 
		
			//! Capacity
			DWORD Capacity()		{	return S_;		}
			//! Actual size
			DWORD Size()			{	return E_ - B_;		}
			//! Pointer to buffer starting address
			BYTE* Begin()			{	return P_ + B_;		}
			//! Pinter to buffer end address
			BYTE* End()			{	return P_ + E_;		}
			//!  Next block
			Block* Next()			{	return N_;		}
			//! Empty or not
			bool IsEmpty()			{	return B_ == E_;	}
			//! Free size
			DWORD FreeSize()		{	return S_ - E_;		}
		};

		//! Used in buffer one-way iterator
		struct Iterator
		{
			BlockBuffer*	P_;		//!< Object pointer
			Block*			B_;		//!< Object pointer
			DWORD			I_;		//!< Current block offset
			DWORD			O_;		//!< Overall buffer offset

			//! Constructor
			Iterator(BlockBuffer *P = NULL, Block* B = NULL, DWORD I = 0, DWORD O = 0) 
				: P_(P), B_(B), I_(I), O_(O)	
			{										
			}
			//! Valid check 
			operator bool()				
			{	
				return B_ && I_<B_->E_&&I_>=B_->B_;	
			}
			//! Whether can it be written 
			bool CanWrite()				
			{	
				return B_ && I_ < B_->S_;			
			}
			//! Reference value
			BYTE& operator *()			
			{	
				CN_ASSERT( P_ && B_ && I_<B_->E_&&I_>=B_->B_);
				return B_->P_[I_];					
			}
			//! Move iterator ++Iter;
			Iterator& operator ++ ()		
			{
				return operator +=(1);			
			}
			//! Move iterator Iter++;
			Iterator& operator ++ (int)	
			{
				return operator +=(1);				
			}
			//! Move iterator  \param dwOffset is offset
			Iterator& operator += (DWORD dwOffset)
			{
				while (dwOffset) 
				{
					if (I_+dwOffset < B_->E_)
						I_ += dwOffset, O_ += dwOffset, dwOffset = 0; 
					else
						dwOffset -= B_->E_-I_, I_ += B_->E_-I_, O_ += B_->E_-I_, B_ = B_->N_, I_ = 0;
				}

				return *this;
			}
			//! Comparision 
			bool operator == (const Iterator& iter)
			{
				return (P_ == iter.P_) && (B_ == iter.B_) && (I_ == iter.I_);
			}
		};
		//! Friend structure
		friend struct Iterator;
		//! Lock
		void Lock()							
		{	
			::EnterCriticalSection(&C_);			
		}
		//! Unlock
		void Unlock()						
		{	
			::LeaveCriticalSection(&C_);			
		}
		//! Autolock
		struct InnerLock
		{
			BlockBuffer* ptr;//!< Object pointer
			///Lock
			InnerLock(BlockBuffer* p) : ptr(p)	
			{
				if (ptr)
					ptr->Lock();						
			}
			///Unlock
			~InnerLock()					
			{
				if (ptr)
					ptr->Unlock();						
			}
		};
		//! Constructor
		BlockBuffer()
		{
			::InitializeCriticalSection(&C_);
			S_ = 0, F_ = L_ = NULL, M_ = CN_COMM_BUFFER_MIN_BLOCK_SIZE;
		}
		//! Destructor to free memory space
		virtual ~BlockBuffer()
		{
			Clear();
			::DeleteCriticalSection(&C_);
		}
		//! Get the starting iterator
		Iterator Begin()					
		{	
			return Iterator(this, F_, F_? F_->B_ : 0, 0);	
		}
		//! Set minimum block siz
		void SetMinBlockSize(DWORD dwMinSize)
		{
			M_ = dwMinSize;
		}
		//! Get minimum block siz
		DWORD GetMinBlockSize()
		{
			return M_;
		}
		//! The number of data byte within buffer
		DWORD Size()						
		{	
			return S_;								
		}
		//! Buffer size
		DWORD SafeSize()						
		{
			InnerLock lock(this);
			return S_;								
		}
		//! Write ANSI character string buffer \sa Write(LPCVOID lpBuf, DWORD dwSize)
		DWORD Write(const char* lpBuf)
		{
			return Write(lpBuf, strlen(lpBuf));
		}
		//! Write UNICODE character string buffer \sa Write(LPCVOID lpBuf, DWORD dwSize)
		DWORD Write(const wchar_t* lpBuf)
		{
			return Write(lpBuf, wcslen(lpBuf)*sizeof(wchar_t));
		}
		//!Write buffer \param[out] lpBuf target buffer \param[in] dwSize Number of data bytes \return value: the actural number of bytes copied  
        DWORD Write(LPCVOID lpBuf, DWORD dwSize)   
		{
			DWORD dwTemp = dwSize, dwFree = FreeSize(), dwCopy = 0;
			
			if (dwFree)//! Check the free space and write data 
			{
				dwCopy = dwFree > dwSize ? dwSize : dwFree;
				memcpy(L_->P_ + L_->E_, lpBuf, dwCopy);
				dwTemp -= dwCopy, L_->E_ += dwCopy;
			}

			if (dwTemp)//! Assign new space for the remainingdata and write them to this spaces   
			{
				memcpy(NewBlock(dwSize)->P_, ((LPBYTE)lpBuf )+ dwCopy, dwTemp);//This source code does not have offset, since the buffer would be large, this is                                                                                                  ignored in the test
				L_->E_ += dwTemp;
			}

			S_ += dwSize;
			return dwSize;
		}
		//! Thread security write buffer \sa Write(LPCVOID lpBuf, DWORD dwSize)
		DWORD SafeWrite(LPCVOID lpBuf, DWORD dwSize)
		{
			InnerLock lock(this);
			return Write(lpBuf, dwSize);
		}
		//! Thread security write ANSI character string buffer \sa Write(LPCVOID lpBuf, DWORD dwSize)
		DWORD SafeWrite(const char* lpBuf)
		{
			InnerLock lock(this);
			return Write(lpBuf, strlen(lpBuf));
		}
		//! Thread security write UNICODE character string buffer \sa Write(LPCVOID lpBuf, DWORD dwSize)
		DWORD SafeWrite(const wchar_t* lpBuf)
		{
			InnerLock lock(this);
			return Write(lpBuf, wcslen(lpBuf)*sizeof(wchar_t));
		}
		//! Copy data \param[out] lpBuf Target buffer \param[in] dwSize: Number of data bytes \param[in] dwStart: Source buffer offset \return value: Number of data bytes actually copied
		DWORD Copy(LPVOID lpBuf, DWORD dwSize, DWORD dwStart = 0)
		{
			CN_ASSERT(lpBuf);

			if (!F_ || !dwSize || dwStart >= S_)
				return 0;

			DWORD dwTemp, dwIndex;
			Block * pCur = F_, *pNext = F_->N_;

			for (dwTemp = 0, dwIndex = pCur->B_; dwTemp < dwStart; pCur = pCur->N_, dwIndex = pCur ? pCur->B_ : 0)
			{
				if (dwStart - dwTemp < pCur->E_ - pCur->B_)
				{
					dwIndex = pCur->B_ + dwStart - dwTemp;
					break;
				}

				dwTemp += pCur->E_ - pCur->B_;
			}

			for (dwTemp = 0; dwTemp < dwSize && pCur; pCur = pCur->N_, dwIndex = pCur ? pCur->B_ : 0)
			{
				if (dwSize - dwTemp < pCur->E_ - dwIndex)
				{
					memcpy((LPBYTE)lpBuf+dwTemp, pCur->P_ + dwIndex, dwSize - dwTemp );
					dwTemp = dwSize;
					break;
				}
				else
				{
					memcpy((LPBYTE)lpBuf+dwTemp, pCur->P_ + dwIndex, pCur->E_ - dwIndex);
					dwTemp += pCur->E_ - dwIndex;
				}
			}

			return dwTemp;
		}
		//! Copy data with thread safety \sa Copy(LPVOID lpBuf, DWORD dwSize, DWORD dwStart=0)  
		DWORD SafeCopy(LPVOID lpBuf, DWORD dwSize, DWORD dwStart = 0)
		{
			InnerLock lock(this);
			return Copy(lpBuf, dwSize, dwStart);
		}
		/*! \param[out] lpBuf: Target buffer, if it is NULL, only delete data, no copy actions  \param[in] dwSize: Number of data bytes to operate \return value: Number of data bytes actually copied or deleted */  
        //! Copy and delete buffer data to lpBuf  
		DWORD Read(LPVOID lpBuf, DWORD dwSize)
		{
			DWORD dwTemp = 0, dwCopy;

			for (Block * pCur = F_, *pNext = NULL; dwTemp<dwSize && pCur; pCur = pNext)
			{
				if (dwSize-dwTemp > pCur->E_-pCur->B_ )
					dwCopy = pCur->E_ - pCur->B_; 
				else
					dwCopy = dwSize - dwTemp;

				if (lpBuf) 
					memcpy((LPBYTE)lpBuf+dwTemp, pCur->P_ + pCur->B_, dwCopy);

				pNext = pCur->N_, dwTemp += dwCopy;

				if (dwCopy == pCur->E_-pCur->B_)
				{//! Delete and recycle completely empty blocks, for the condition that less than 4*CN_COMM_BUFFER_MIN_BLOCK_SIZE, 1 block will be reserved 
					if (pNext || pCur->S_>(M_<<2))
					{
						delete[] (BYTE*)pCur;
						F_ = pNext;
						if (!F_)
							L_ = NULL;
					}
					else
						pCur->B_ = pCur->E_ = 0;
				}
				else
					pCur->B_ += dwCopy;

				S_ -= dwCopy;
			}

			return dwTemp;
		}
		//! Read ANSI character string buffer
		char* ReadString(char* lpBuf, DWORD dMaxSize)
		{
			lpBuf[Read(lpBuf, dMaxSize)] = '\0';
			return lpBuf;
		}
		//! Read UNICODE charactr string buffer
		wchar_t* ReadString(wchar_t* lpBuf, DWORD dMaxSize)
		{
			lpBuf[(Read(lpBuf, dMaxSize*sizeof(wchar_t))+1) / sizeof(wchar_t)] = L'\0';
			return lpBuf;
		}
		//! Read function with thread safety
		DWORD SafeRead(LPVOID lpBuf, DWORD dwSize)
		{
			InnerLock lock(this);
			return  Read(lpBuf, dwSize);
		}
		//! Read ANSI character string buffer with thread safety
		char* SafeReadString(char* lpBuf, DWORD dMaxSize)
		{
			InnerLock lock(this);
			return ReadString(lpBuf, dMaxSize);
		}
		//! Read UNICODE character string buffer with thread safety
		wchar_t* SafeReadString(wchar_t* lpBuf, DWORD dMaxSize)
		{
			InnerLock lock(this);
			return ReadString(lpBuf, dMaxSize);
		}
		//! Clear \param bDeleteAll: If it is true, release all the space, or else reserve one memory block to enhance efficiency
		void Clear(bool bDeleteAll = false)
		{
			if (F_ && (F_==L_) && F_->S_>(M_<<2))
			{
                DWORD S = F_->S_;
                memset(F_, 0, sizeof(Block)), F_->S_ = S;
            }
			else
			{
				for (Block* t = F_;  t; delete  F_)
					F_ = t, t = t->N_;

				F_ = L_ = NULL, S_ = 0;
			}
		}
		//! Clear function with thread safety  \sa void Clear(bool bDeleteAll = false)
		void SafeClear(bool bDeleteAll = false)
		{
			InnerLock lock(this);
			Clear(bDeleteAll);
		}
		//! Get memory block pointer \param bFirst: When it is true, get link list header pointer, or else get the link list tail pointer 
		Block* GetBlockPtr(bool bFirst = true)	
		{	
			return bFirst ? F_ : L_;						
		}
		//! Free space in the button of buffer
		DWORD FreeSize()					
		{	
			return L_ ? L_->S_-L_->E_ : 0 ;			
		}
		//! Get API insert data direct assess buffer, specify the required memory space in dwSize, assign new blocks if the free space is not enough, specify 0 will return a pointer to the free space at the end of the buffer, if there is no free space, returns a NULL  
		LPVOID GetFreePtr(DWORD dwSize = 0)
		{
			if (dwSize)
				return FreeSize() > dwSize ? L_->P_ + L_->E_ : NewBlock(dwSize)->P_;
			else
				return L_ ? L_->P_ + L_->E_ : NULL;
		}
		/*! \param[in] dwSize specify the value to add, not the full value 
                    \code strcpy(GetFreePtr(100), "test"), Release(4); //Get a 100 bytes buffer, write in 4 bytes of data, adjust size and increase it for 4 byts \endcode  
                */  
                //! Use API directly write data, and use GetFreePtr() to get the pointer of free room and adjust the size of the buffer at the same time. 
                DWORD Release(DWORD dwSize)
		{
			return (dwSize <= L_->S_) ? (L_->E_ += dwSize, S_ += dwSize, dwSize) : 0;		
		}
		//! Get the buffer pointer of the first vaild block
		LPVOID GetPtr()
		{
			return F_ ? F_->P_ + F_->B_ : NULL;
		}
		//! Get the first vaild block pointer to data size
		DWORD GetPtrSize()
		{
			return F_ ? F_->E_ - F_->B_ : 0;
		}
		//! Array access, if the number of blocks is more than 1 and the efficiency is low, iterator access would have better performance 
		BYTE& operator[](DWORD dwOffset)
		{
			CN_ASSERT(F_);

			if (F_ == L_)	
			{
				CN_ASSERT(F_->S_ > dwOffset);
				return F_->P_[dwOffset];
			}
			else			
			{
				Iterator iter = Begin();
				iter += dwOffset;
				CN_ASSERT(iter);
				return *iter;
			}
		}
		
	protected:

		//! Create a new block, and add it to the tail
		Block* NewBlock(DWORD dwSize)
		{
			dwSize = dwSize < M_ ? M_ : dwSize;
			Block * pNew = (Block *) new BYTE[sizeof(Block) - 4 + dwSize];

			if (pNew)
			{
				memset(pNew, 0, sizeof(Block));
				pNew->S_ = dwSize;

				if (L_)
					L_->N_ = pNew, L_ = pNew;
				else
					F_ = L_ = pNew;
			}
			
			return pNew;
		}

		Block*				F_;//!< Header pointer
		Block*				L_;//!< Tail pointer
		DWORD				S_;//!< Size
		DWORD				M_;//!< Minmum length of the block
		CRITICAL_SECTION	        C_;//!< Lock structure
	};

#if defined(CN_COMM_MFC_EXCEPTION)
	/*! Define macro CN_COMM_MFC_EXCEPTION CN_ASSERT as required \n Use throw new MfcException(msg); to assert MFC exception (VC++) */
	//! Used in MFC expection  
	class MfcException : public CException
	{
	public:
		//! Comstructor function, CException automatic destructure is required
		MfcException(LPCTSTR szMsg) : CException(TRUE)
		{
			lstrcpy(szMsg_, szMsg);

		#ifdef _DEBUG
			CException::m_bReadyForDelete = TRUE;
		#endif
		}
		//! Error information
		BOOL GetErrorMessage( LPTSTR lpszError, UINT nMaxError, PUINT pnHelpContext = NULL )
		{
		#ifndef CN_COMM_FOR_CE
			lstrcpyn(lpszError, szMsg_, nMaxError);
		#else
			_tcsncpy(lpszError, szMsg_, nMaxError - 1);
			lpszError[nMaxError - 1] = _T('\0');
		#endif
			return TRUE;
		}
	protected:
		TCHAR szMsg_[256]; //!< Error message, format: CN_ASSERT(expression) failed; CnComm(line number); Code(GetLastError()); description of error  
	};
#endif

	//! Assert exception if user enable exception; if exception is disabled, for DEBUG version and console version, output error information and exit; for Release version, popup notice dialogue and response to user requirement
	static bool Assert(LPCTSTR szMessage)
	{
	#if defined(_DEBUG) || defined(CN_COMM_STD_EXCEPTION) || defined(CN_COMM_VCL_EXCEPTION) || defined(CN_COMM_MFC_EXCEPTION)
		TCHAR szMsg[256];
		DWORD dwError, dwLength;
		
		_tcscpy(szMsg, szMessage);

		dwError = GetLastError();
		if (dwError)//! Error code (GetLastError())is not 0  output error description  
		{
			dwLength = _tcslen(szMsg);
			_sntprintf(szMsg + dwLength, 256 - _tcslen(szMsg), _T("Code:%d; "), dwError);
			dwLength = _tcslen(szMsg);

			FormatMessage(
				FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL,
				dwError,
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	//! The error description adoptes the local language settings. 
				szMsg + dwLength - 1,							
				256 - dwLength - 1,									
				NULL
				);
		}
	#else 
		;// Release Version, do nothing, reserved
	#endif

	#if	defined(CN_COMM_STD_EXCEPTION)

		#ifdef _UNICODE
			char szTemp[512] = {0};
			WideCharToMultiByte(CP_ACP, 0, szMsg, -1, szTemp, wcslen(szMsg)*sizeof(wchar_t), NULL, NULL);
			throw std::runtime_error(szTemp);
		#else
			throw std::runtime_error(szMsg);
		#endif

	#elif defined(CN_COMM_VCL_EXCEPTION)

		throw Exception(szMsg);

	#elif defined(CN_COMM_MFC_EXCEPTION)

		throw (new MfcException(szMsg));

	#elif defined(_DEBUG)

		OutputDebugString(szMsg);
		#ifdef _CONSOLE
			// Require setlocale(LC_ALL, "chs"); so that console can output UNICODE Chinese, converting to ANSI should here should avoid this kind of issues 
			#ifdef _UNICODE
				char szTemp[512] = {0};
				WideCharToMultiByte(CP_ACP, 0, szMsg, -1, szTemp, wcslen(szMsg)*sizeof(wchar_t), NULL, NULL);
				fprintf(stderr, szTemp);
			#else
				fprintf(stderr, szMsg);
			#endif

			system("pause");
			exit(1);
		#else
			switch(MessageBox(NULL, szMsg, _T("CN_ASSERT"), MB_ABORTRETRYIGNORE))
			{
			case IDABORT:
				exit(1);

			case IDRETRY:
				DebugBreak();
				break;

			case IDIGNORE:
				break;
			}
		#endif

	#else 
		;// Release Version, do nothing, reserved
	#endif
		return false;
	}

protected:

	DWORD				dwOption_;		//!< Important  mode configuration, specifyed in the constructor
	HANDLE				hComm_;			//!< Serial port handle
	DWORD				dwPort_;		//!< Serial port number. 0 for special serial port, -1 of not valid, mainly detected by message notifications 
	TCHAR				szName_[64];		//!< Store serial port name, such as COM1
	CRITICAL_SECTION	CS_;				//!< Critical section mutual exclusion lock
	DCB					DCB_;		//!< Baud rate, stop bit, and so on   
	COMMTIMEOUTS		CO_;				//!< Over time constructor 
	COMMPROP*			pCP_;			//!< seiral port parameter

	BlockBuffer			I_;			//!< Read buffer
	BlockBuffer			O_;			//!< Write buffer
	DWORD				dwInCount_;		//!< Read count
	DWORD				dwOutCount_;		//!< Write count

	bool				bContinue_;		//!< Thread continue running loop flag
	DWORD				dwWaitEvent_;		//!< WaitCommEvent event

	HANDLE				hWatchThread_;		//!< Watch auxiliary thread
	DWORD				hWatchThreadId_;	//!< Watch auxiliary thread ID
	HANDLE				hWatchEvent_;		//!< Notification event to watch thread

	HANDLE				hReadThread_;		//!< Receiving auxiliary thread for duplexing operation to enhance efficiency
	DWORD				hReadThreadId_;		//!< Receiving uxiliary thread ID
	HANDLE				hReadEvent_;		//!< Notification event for receiving auxiliary thread

	HANDLE				hWriteThread_;		//!< Transmitting auxiliary thread for duplexing operation to enhance efficiency
	DWORD				hWriteThreadId_;	//!< Transmitting auxiliary thread  ID
	HANDLE				hWriteEvent_;		//!< Notification event for transmitting auxiliary thread

	HWND				hNotifyWnd_;		//!< Notify window
	DWORD				hNotifyThreadId_;	//!< Notify thread
	DWORD				dwNotifyNum_;		//!< Send out notification after receiving a number of bytes(>=dwNotifyNum_) 

#ifndef CN_COMM_FOR_CE
	OVERLAPPED			RO_;				//!< Overlapped I/O ReadFile
	OVERLAPPED			WO_;				//!< Overlapped I/O WriteFile
	OVERLAPPED			EO_;				//!< Overlapped I/O WaitCommEvent 
#endif

	//! Initialization
	virtual void Init() 
	{
		::InitializeCriticalSection(&CS_);

		memset(szName_, 0, 64*sizeof(TCHAR));

		memset(&DCB_,	0, sizeof(DCB_));
		DCB_.DCBlength	= sizeof(DCB_);

		hComm_				= INVALID_HANDLE_VALUE;
		dwPort_				= (DWORD)-1;
		dwOutCount_			= 0;
		dwInCount_			= 0;
		
		dwWaitEvent_		= CN_COMM_WAIT_EVENT;
		hWatchThread_		= NULL;
		hReadThread_		= NULL;
		hWriteThread_		= NULL;
		hWatchThreadId_		= 0;
		hReadThreadId_		= 0;
		hWriteThreadId_		= 0;
		
		hNotifyWnd_			= NULL;
		dwNotifyNum_		= 0;
		hNotifyThreadId_	= 0;
		pCP_				= NULL;

	#ifndef CN_COMM_FOR_CE
		memset(&RO_, 0, sizeof(RO_));
		memset(&WO_, 0, sizeof(WO_));
		memset(&EO_, 0, sizeof(EO_));
		
		RO_.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		CN_ASSERT(RO_.hEvent != NULL); 
		
		WO_.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		CN_ASSERT(WO_.hEvent != NULL);
		
		EO_.hEvent= ::CreateEvent(NULL, TRUE, FALSE, NULL);
		CN_ASSERT(EO_.hEvent != NULL); 
	#endif

		hWatchEvent_	= ::CreateEvent(NULL, TRUE, FALSE, NULL);
		CN_ASSERT(hWatchEvent_ != NULL); 
		hReadEvent_		= ::CreateEvent(NULL, TRUE, FALSE, NULL);
		CN_ASSERT(hReadEvent_ != NULL); 
		hWriteEvent_	= ::CreateEvent(NULL, TRUE, FALSE, NULL);
		CN_ASSERT(hWriteEvent_ != NULL); 
	} 
	//! Destructor
	virtual void Destroy()
	{
	#ifndef CN_COMM_FOR_CE
		if(RO_.hEvent != NULL)
			CloseHandle(RO_.hEvent);
		
		if(WO_.hEvent != NULL)
			CloseHandle(WO_.hEvent);
		
		if(EO_.hEvent != NULL)
			CloseHandle(EO_.hEvent);
	#endif

		if(hWatchEvent_ != NULL)
			CloseHandle(hWatchEvent_);

		if(hReadEvent_ != NULL)
			CloseHandle(hReadEvent_);

		if(hWriteEvent_ != NULL)
			CloseHandle(hWriteEvent_);

		if (pCP_)
			delete[] ((BYTE*)pCP_);

		::DeleteCriticalSection(&CS_);
	}
	//! Bind with serial port 
	void BindPort(DWORD dwPort)
	{
		dwPort_ = dwPort;

	#if defined(CN_COMM_FOR_CE)
		wsprintf(szName_, _T("COM%d:"), dwPort);
	#else
		wsprintf(szName_, _T("\\\\.\\COM%d"), dwPort);
	#endif
	}
	//! Open the serial port. 
	virtual bool OpenPort()
	{
		CN_ASSERT(!IsOpen());

		if(IsOpen())
			Close();
		
		hComm_ = ::CreateFile(
			szName_,
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL | (IsOverlappedMode() ? FILE_FLAG_OVERLAPPED : 0),
			NULL
			);
		
		return IsOpen();
	}
	//! Set serial port 
	virtual bool SetupPort()
	{
		if(!CN_ASSERT(IsOpen()))
			return false;

		#if defined(CN_COMM_FOR_CE)
			::SetupComm(hComm_, 4096, 4096);
		#else
			if(!CN_ASSERT(::SetupComm(hComm_, 4096, 4096)))//! Configure port transmission queue size, read queue 4096 bytes, and write queue 4096 bytes; for blocked I/O mode, the transmission queue is not valid   
			return false;   // Some hardware does not support this
		#endif
		
		if(!CN_ASSERT(::GetCommTimeouts(hComm_, &CO_)))
			return false;

		CO_.ReadIntervalTimeout		= 100;//! Configure timeout structure, minimum interval between two characters is 100ms 
		CO_.ReadTotalTimeoutMultiplier	= 0;
		CO_.ReadTotalTimeoutConstant	= IsOverlappedMode() ? 500 : 250;//! Time out for read, 500ms for Overlapped I/O mode, 250ms for blocked I/O mode
		CO_.WriteTotalTimeoutMultiplier = IsOverlappedMode() ? 1 : 0;
		CO_.WriteTotalTimeoutConstant	= IsOverlappedMode() ? 10000 : 250;//! Time out for write, (10000+1*Number of bytes) for Overlapped I/O mode, 250ms for blocked                                                                                        I/O mode  
		if(!CN_ASSERT(::SetCommTimeouts(hComm_, &CO_)))
			return false; 
		
		if(!CN_ASSERT(::PurgeComm(hComm_, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR )))//! Clear port
			return false; 
		
		return true;
	} 
	//! Read data from port to buffer
	DWORD PortToBuffer(DWORD dwPortByteNum)
	{
		BlockBuffer::InnerLock locker(&I_);
		return dwPortByteNum ? I_.Release(ReadPort(I_.GetFreePtr(dwPortByteNum), dwPortByteNum)) : 0;
	}
	//! Write data from buffer to port 
	DWORD BufferToPort(DWORD dwMaxSize = 1024)
	{
		if (IsOverlappedMode())
		{
			BlockBuffer::InnerLock locker(&O_);

			if (!O_.Size())
				return 0;

			DWORD dwWrite = dwMaxSize > O_.GetPtrSize() ? O_.GetPtrSize() : dwMaxSize;
			WritePort(O_.GetPtr(), dwWrite);
			return O_.Read(NULL, dwWrite);
		}
		else
		{
			BYTE btTemp[1024];
			DWORD dwLength, dwIndex = 0;

			O_.Lock();
			dwLength = O_.Read(btTemp, 1024);
			O_.Unlock();

			while ( dwIndex < dwLength )
				dwIndex += WritePort(btTemp + dwIndex, dwLength - dwIndex);

			return dwLength;
		}
	}
	/*! \param uMsg: Message \param lParam: Parameter \param bPostMode: Use PostMessage or SendMessage to send out message*/  
        //! When window handle or thread ID is valid, send out message to notify corresponding events, by default, WPARAM contians the serial port number  
	virtual void Notify(UINT uMsg, LPARAM lParam = 0, bool bPostMode = true)
	{
		if(hNotifyWnd_ && ::IsWindow(hNotifyWnd_))// Window handle vaild
		{
			if (bPostMode)
				PostMessage(hNotifyWnd_, uMsg, WPARAM(dwPort_), lParam);
			else
				SendMessage(hNotifyWnd_, uMsg, WPARAM(dwPort_), lParam);
		}

		if (hNotifyThreadId_)// Thread ID vaild
			PostThreadMessage(hNotifyThreadId_, uMsg, WPARAM(dwPort_), lParam);
	}
	//---------------------------------------threads callback-----------------------------------------------------
	//! Response to EV_RXCHAR event, callback function called by thread, void function can be expanded from base class (recommended)     
	virtual void OnReceive() 
	{
		Notify(ON_COM_RECEIVE);
	}
	//! Response to EV_DSR event
	virtual void OnDSR()
	{
		Notify(ON_COM_DSR, CheckDSR() ? 1 : 0);
	}
	//! Response to EV_CTS event
	virtual void OnCTS()
	{
		Notify(ON_COM_CTS, CheckCTS() ? 1 : 0);
	}
	//! Response to EV_BREAK event
	virtual void OnBreak()
	{
		ClearCommBreak(hComm_);
		Notify(ON_COM_BREAK);
	}
	//! Response to EV_TXEMPTY event
	virtual void OnTxEmpty()
	{
		Notify(ON_COM_TXEMPTY);
	}
	//! Response to EV_ERROR event
	virtual void OnError()
	{
		Notify(ON_COM_ERROR, ClearError());
	}
	//! Response to EV_RING event
	virtual void OnRing()
	{
		Notify(ON_COM_RING, CheckRING() ? 1 : 0);
	}
	//! Response to EV_RLSD event
	virtual void OnRLSD()
	{
		Notify(MS_RLSD_ON, CheckRLSD() ? 1 : 0);
	}
	//! Response to EV_RXFLAG event
	virtual void OnRxFlag()
	{
		Notify(ON_COM_RXFLAG);
	}
	//! Response to EV_POWER event
	virtual void OnPower()
	{
		Notify(ON_COM_POWER);
	}
	//! Response to EV_RX80FULL event
	virtual void OnRx80Full()
	{
		Notify(ON_COM_RX80FULL);
	}
	//! Response to EV_EVENT1 event 
	virtual void OnEvent1()
	{
		Notify(ON_COM_EVENT1);
	}
	//! Response to EV_EVENT2 event
	virtual void OnEvent2()
	{
		Notify(ON_COM_EVENT2);
	}
	//! Response to EV_PERR event
	virtual void OnPrintErr()
	{
		Notify(ON_COM_PERR);
	}
	//!  Common event handle
	virtual void HandleEvent(DWORD dwMask)
	{
		if ((dwMask & EV_RXCHAR) && !(dwOption_&EN_RX_THREAD))
		{
			DWORD dwLength = GetQueueCount(true);

			if (dwLength != 0 && dwLength != 0xFFFFFFFF)  /* Enhance stability */
			{
				if (dwOption_ & EN_RX_BUFFER)
				{
					PortToBuffer((dwLength << 1) + 64);// Additional bytes to avoid traps on gaps between events

					if (I_.Size() >= dwNotifyNum_)
						OnReceive();
				}
				else
				{
					if (dwLength >= dwNotifyNum_)
						OnReceive();
				}
			}
		}

		if (dwMask & EV_TXEMPTY)
		{
			if ((dwOption_&EN_TX_BUFFER) && !(dwOption_&EN_TX_THREAD) && O_.SafeSize())
				BufferToPort();

			OnTxEmpty();
		}

		if (dwMask & EV_RXFLAG)
			OnRxFlag();

		if (dwMask & EV_CTS)
			OnCTS();
			
		if (dwMask & EV_DSR)
			OnDSR();
			
		if (dwMask & EV_RING)
			OnRing();
			
		if (dwMask & EV_RLSD)
			OnRLSD();
			
		if (dwMask & EV_BREAK)
			OnBreak();
			
		if (dwMask & EV_ERR)
			OnError();

	#ifdef CN_COMM_FOR_CE
		if (dwMask & EV_POWER)
			OnPower();
	#endif
	}
	
#ifndef CN_COMM_FOR_CE
	//! Monitoring thread used in overlepped I/O model 
	virtual DWORD OverlappedModel()
	{
		if(!CN_ASSERT(IsOpen()))
			return 1;

		if(!CN_ASSERT(::SetCommMask(hComm_, dwWaitEvent_)))
			return 1;

		for(DWORD dwMask = 0, dwLength; bContinue_ && IsOpen(); dwMask = 0)
		{
			if (GetQueueCount(true) != 0xFFFFFFFF && GetQueueCount(true))//! Wait event will scan whether there is pending data first, if there is pending event, it similates an EV_RXCHAR event to avoid event notification trap.  
				dwMask = EV_RXCHAR, Sleep(10);
			else
			{
				if(!::WaitCommEvent(hComm_, &dwMask, &EO_))
				{
					if(::GetLastError() == ERROR_IO_PENDING)
						::GetOverlappedResult(hComm_, &EO_, &dwLength, TRUE);
					else
					{
						Sleep(10);
						continue;
					}
				}
			}
			
			if(dwMask == 0)
			{
				if ((dwOption_&EN_TX_BUFFER) && !(dwOption_&EN_TX_THREAD) && O_.SafeSize())
					BufferToPort();

				continue;
			}
			
			HandleEvent(dwMask);
		}//for

		return 0;
	}
#endif
	//! Watch thread for blocked I/O mode   
	virtual DWORD NonoverlappedModel()
	{
		if(!CN_ASSERT(IsOpen()))
			return 1;

		for (DWORD dwReturn; bContinue_ && IsOpen();)
		{
			dwReturn = WaitForSingleObject(hWatchEvent_, 50);

			if (!bContinue_) 
				break;

			switch (dwReturn)
			{
			case WAIT_OBJECT_0:
				while ((dwOption_&EN_TX_BUFFER) && O_.SafeSize()) {
					//Some of the virtual serial ports donot support DCB hardware flow control configuration. So here we use manual hardware flow control
					if (DCB_.fOutxCtsFlow == TRUE && CheckCTS() == FALSE) {
						Sleep(50);
						if (bContinue_)
							continue;
						else 
							break;

					}		
					BufferToPort(1); // Private modification 1	
				}

				ResetEvent(hWatchEvent_);

				break;

			case WAIT_TIMEOUT:

				DWORD dwLength = GetQueueCount(true);

				if (dwLength != 0xFFFFFFFF && dwLength != 0)  /* Private modification, add != 0xFFFFFFFF */
				{
					if (dwOption_ & EN_RX_BUFFER)
					{
						PortToBuffer(dwLength);

						if (I_.Size() >= dwNotifyNum_)
							OnReceive();
					}
					else
					{
						if (dwLength >= dwNotifyNum_)
							OnReceive();
					}
				}

				if ( (dwOption_&EN_TX_BUFFER) && O_.SafeSize() )
					BufferToPort();

				break;
			}
		}

		return 0;
	}
#ifdef CN_COMM_FOR_CE
	//! Monitoring thread used in I/O modle under WINCE (It is overlapped mode + blocking mode)
	virtual DWORD EmbeddedModel()
	{
		if(!CN_ASSERT(IsOpen()))
			return 1;

		if(!CN_ASSERT(::SetCommMask(hComm_, dwWaitEvent_)))
			return 1;

		for(DWORD dwMask = 0; bContinue_ && IsOpen(); dwMask = 0)
		{
			if (GetQueueCount(true) != 0xFFFFFFFF && GetQueueCount(true))//! Wait event will scan whether there is pending data first, if there is pending event, it similates an EV_RXCHAR event to avoid event notification trap.   
				dwMask = EV_RXCHAR, Sleep(10);
			else
			{
				if(!::WaitCommEvent(hComm_, &dwMask, NULL))
					continue;
			}
			
			if(dwMask == 0)
			{
				if ( (dwOption_&EN_TX_BUFFER) && !(dwOption_&EN_TX_THREAD) && O_.SafeSize())
					BufferToPort();

				continue;
			}
		
			HandleEvent(dwMask);

		}//for

		return 0;
	}
#endif
	//! Used in duplexing processing  It also can be used in other purpose 
	virtual DWORD ReadModel()
	{
		while(bContinue_)
		{
			Sleep(50);

			DWORD dwLength = GetQueueCount(true);

			if (dwLength != 0xFFFFFFFF && dwLength != 0)  /* Private modification, add  != 0xFFFFFFFF */
			{
				if (dwOption_ & EN_RX_BUFFER)
				{
					PortToBuffer(dwLength);

					if (I_.SafeSize() >= dwNotifyNum_)
						OnReceive();
				}
				else
				{
					if (dwLength >= dwNotifyNum_)
						OnReceive();
				}
			}
		}

		return 0;
	}
	//! For duplexing, of cause you can change its purpose  
	virtual DWORD WriteModel()
	{
		CN_ASSERT(dwOption_ & EN_TX_BUFFER);

		while (bContinue_)
		{
			DWORD dwReturn = ::WaitForSingleObject(hWriteEvent_, 50); //Original value: 200£¬modify to 50

			while ((dwOption_&EN_TX_BUFFER) && O_.SafeSize()) {
				//Some of the virtual serial ports donot support DCB hardware flow control configuration  So here we use manual hardware flow control
				if (DCB_.fOutxCtsFlow == TRUE && CheckCTS() == FALSE) {
					Sleep(50);
					if (bContinue_)
						continue;
					else 
						break;
				}		
				BufferToPort(1); //Modify to 1	
			}

			if (dwReturn == WAIT_OBJECT_0)
				ResetEvent(hWriteEvent_);
		}

		return 0;
	}
	
private:  
	//! Copy constructor is not allowed
	CnComm(const CnComm&);
	//! Assigning value to function is not allowed
	CnComm &operator = (const CnComm&);

#if defined(_MT) && !defined(CN_COMM_FOR_CE)
	//! Watch Thread completes the watching, determine whether to complete the read or write operation based on configuration 
	static UINT APIENTRY WatchThreadProc(LPVOID lpPara)
#else
	//! Watch Thread completes the watching, determine whether to complete the read or write operation based on configuration 
	static DWORD WINAPI WatchThreadProc(LPVOID lpPara)
#endif
	{
	#ifdef CN_COMM_FOR_CE 
		DWORD dwCode = ((CnComm *)lpPara)->EmbeddedModel();
	#else
		DWORD dwCode = ((CnComm *)lpPara)->IsOverlappedMode() 
			? ((CnComm *)lpPara)->OverlappedModel() 
			: ((CnComm *)lpPara)->NonoverlappedModel();

		#if defined(_MT) 
			_endthreadex(dwCode);
		#endif
	#endif

		return dwCode;
	}

#if defined(_MT) && !defined(CN_COMM_FOR_CE)
	//! Duplexing thread, not used yet
	static UINT APIENTRY ReadThreadProc(LPVOID lpPara)
#else
	//! Duplexing thread, not used yet
	static DWORD WINAPI ReadThreadProc(LPVOID lpPara)
#endif
	{
		DWORD dwCode = ((CnComm *)lpPara)->ReadModel();

		#if defined(_MT) && !defined(CN_COMM_FOR_CE)
			_endthreadex(dwCode);
		#endif

		return dwCode;
	}

#if defined(_MT) && !defined(CN_COMM_FOR_CE)
	//! Duplexing thread, can be used by WINCE
	static UINT APIENTRY WriteThreadProc(LPVOID lpPara)
#else 
	//! Duplexing thread, can be used by WINCE
	static DWORD WINAPI WriteThreadProc(LPVOID lpPara)
#endif
	{
		DWORD dwCode = ((CnComm *)lpPara)->WriteModel();

		#if defined(_MT) && !defined(CN_COMM_FOR_CE)
			_endthreadex(dwCode);
		#endif

		return dwCode;
	}
};

#endif //! _CN_COMM_H_
