/*! \mainpage CnComm v1.51 多线程串口通讯库
* \section About 关于
*
* \n 版本: CnComm v1.51
* \n 用途: WINDOWS/WINCE 多线程串口通讯库
* \n 语言: C++ (ANSI/UNICODE)
* \n 平台: WINDOWS(WIN98/NT/2000/XP/2003/Vista); WINCE 5.0 模拟器; Pocket PC 2003 模拟器;
* \n 硬件: PC串口; 串口服务器; USB串口; 虚拟串口;
* \n 编译: 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 作者: llbird
* \n 邮箱: wushaojian@21cn.com
* \n 博客: http://blog.csdn.net/wujian53 http://www.cppblog.com/llbird
* \n 维护: 2002.10 - 2009.8
*
* \section Announce 说明
* \n 1) 可以自由使用及传播, 请保留相关声明;
* \n 2) 不推荐直接在本代码上修改, 应通过C++继承扩展机制扩展本代码;
* \n 3) 如果您直接修改本代码, 请发一份给我,便于同网友分享您有益的改动;
* \n 4) 不兼容cnComm1.4以下版本, 有很大改动,同时也更名CnComm;
* \n 5) 还是那句老话, 水平有限, 错误在所难免, 欢迎来信指正, 收入有限, 时间有限, 不提供除CnComm内部问题外的咨询;
*
* \section Log 日志
* \n 2009 v1.51 修正版; 考虑到将来的工作中可能不会再和串口打交道,这很可能是最后一版;
* \n 2009 v1.5 增加内置分块链表缓冲区; 增加对WINCE的支持(模拟器下测试通过);
* \n 2008 v1.4 增加对同步IO的多线程支持; 增加C++异常的支持; 改名CnComm; Cn == C Next;
* \n 2007 v1.3 细节部分修订;
* \n 2006 v1.2 细节部分修订;
* \n 2005 v1.1 细节部分修订;
* \n 2004 v1.0 采用VC命名风格(匈牙利), 在多个WINDOW平台、编译器测试通过, 首次公开发布cnComm;
* \n 2002 v0.1 因工作需要开发串口通讯基础类, 传统C++的继承机制, 传统C命名风格;
*/
#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 //!< 配置WINCE的支持
#endif
#ifndef CN_COMM_FOR_CE
#include <mcx.h>
#include <process.h> // WINCE没有process.h
#endif
#ifndef ON_COM_MSG_BASE
#define ON_COM_MSG_BASE WM_USER + 618 //!< 消息编号的基点
#endif
// 送到窗口的消息 WPARAM COM端口号
#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 默认的等待事件| 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 默认的等待事件| EV_RXFLAG
#endif
#endif
#ifndef CN_COMM_BUFFER_MIN_BLOCK_SIZE
#define CN_COMM_BUFFER_MIN_BLOCK_SIZE 1024 //!< 定义缓冲区块的最小值
#endif
#if CN_COMM_BUFFER_MIN_BLOCK_SIZE < 4
#error CN_COMM_BUFFER_MIN_BLOCK_SIZE must >= 4 //!< 缓冲区块的最小值不允许小于4
#endif
#ifndef CN_ASSERT
#define CN_2STR(L) _T(#L) //!< 将表达式L转换成字符串
#define CN_LINE(L) CN_2STR(L) //!< 将行号L转换成字符串
/*! 内部断言 启用异常将抛出异常 否则调试版将退出 发行版未启用异常将不做任何处理 */
#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 将抛出标准C++异常
#ifdef CN_COMM_STD_EXCEPTION
#include <stdexcept> //throw runtime_error(msg)
#endif
//CN_COMM_VCL_EXCEPTION CN_ASSERT 将抛出VCL异常(C++ Builder)
#if defined(CN_COMM_VCL_EXCEPTION) && defined(__BORLANDC__)
#include <vcl.h> //throw new Exception(msg)
#endif
//CN_COMM_MFC_EXCEPTION CN_ASSERT 将抛出MFC异常(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) 多线程串口通讯基础库
\example doc_0.cpp 例子0 \example doc_1.cpp \example doc_2.cpp \example doc_3.cpp \example SerialDlg.cpp
*/
class CnComm
{
public:
//! 临界区
struct InnerLock;
//! 缓冲区类
class BlockBuffer;
//! MFC异常
class MfcException;
//! 用于配置模式的枚举值, 32位掩码
enum OptionEnum
{
EN_THREAD = 0x00000001, //!< 启用监视线程 伴随串口打开启动 WatchThread
EN_OVERLAPPED = 0x00000002, //!< 启用异步重叠IO方式
EN_RX_BUFFER = 0x00000004, //!< 启用读缓冲
EN_TX_BUFFER = 0x00000008, //!< 启用写缓冲
EN_RX_THREAD = 0x00000010, //!< 启动读线程 暂时未用 ReadThread
EN_TX_THREAD = 0x00000020, //!< 启动写线程 用于WINCE的双工操作 应同时启用写缓冲 伴随串口打开启动 WriteThread
EN_SUSPEND = 0x00000040, //!< 启动线程时暂停
EN_ABOVE_NORMAL = 0x00000080, //!< 启动线程优先级高一个级别
EN_FLUSH = 0x00000100, //!< 当关闭串口时输出队列未发送完的数据(端口缓冲区) 并阻塞等待
EN_FLUSH_ALL = 0x00000200 //!< 同上(包括写缓冲及端口队列) 您如果重载了写模块而又没有写好 可能导致线程挂起无法正常关闭
};
//! 构造函数 配置具体应用模式 \param[in] dwOption 根据需要由OptionEnum组合而成
#ifdef CN_COMM_FOR_CE
//! WINCE:默认打开串口时启动监视线程 启用写独立线程 启用写缓冲
CnComm(DWORD dwOption = EN_THREAD )
#else
//! WIN32:默认打开串口时启动监视线程 异步重叠方式
CnComm(DWORD dwOption = EN_THREAD | EN_OVERLAPPED)
#endif
{
Init();
SetOption(dwOption);
}
//! 另一模式构造 兼容cnComm1~1.3 \param[in] bThread 启动监视线程 \param[in] bOverlapped 启用重叠I/O
CnComm(bool bThread, bool bOverlapped)
{
DWORD dwOption = 0;
if (bThread)
dwOption |= EN_THREAD;
if (bOverlapped)
dwOption |= EN_OVERLAPPED;
Init();
SetOption(dwOption);
}
//! 析构 自动关闭串口
virtual ~CnComm()
{
Close();
Destroy();
}
//! 判断串口是或打开
bool IsOpen()
{
return hComm_ != INVALID_HANDLE_VALUE;
}
//! 判断串口是或打开
operator bool ()
{
return hComm_ != INVALID_HANDLE_VALUE;
}
//! 获得串口句炳
HANDLE GetHandle()
{
return hComm_;
}
//! 获得串口句炳
operator HANDLE()
{
return hComm_;
}
//! 获得串口序号
DWORD GetPort()
{
return dwPort_;
}
//! 获得串口全名
LPCTSTR GetPortName()
{
return szName_;
}
//! 获得CnComm的基本配置参数 返回32位配置掩码
DWORD GetOption()
{
return dwOption_;
}
//! 设置CnComm的基本配置参数 在打开串口前设置有意义 \param[in] dwOption 32位配置掩码
void SetOption(DWORD dwOption)
{
CN_ASSERT(!IsOpen());//! 打开状态下不可以设置参数
dwOption_ = dwOption;
#ifdef CN_COMM_FOR_CE
CN_ASSERT(!IsOverlappedMode()); //! WINCE不允许使用重叠IO 即EN_OVERLAPPED掩码
dwOption_ &= (~EN_OVERLAPPED);
#endif
}
//! 修改CnComm的基本配置参数 在打开串口前设置有意义 \param[in] dwRemove 删除的32位配置掩码 \param[in] dwAdd 添加的32位配置掩码
void ModifyOption(DWORD dwRemove, DWORD dwAdd)
{
CN_ASSERT(!IsOpen());//! 打开状态下不可以设置参数
dwOption_ &= ~dwRemove;
dwOption_ |= dwAdd;
#ifdef CN_COMM_FOR_CE
CN_ASSERT(!IsOverlappedMode()); //! WINCE不允许使用重叠IO 即EN_OV