You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2154 lines
53 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*! \mainpage ComComm v1.51 多线程串口通讯库
* \section About 关于
*
* \n 版本: ComComm 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以下版本, 有很大改动同时也更名ComComm;
* \n 5) 还是那句老话, 水平有限, 错误在所难免, 欢迎来信指正, 收入有限, 时间有限, 不提供除ComComm内部问题外的咨询;
*
* \section Log 日志
* \n 2009 v1.51 修正版; 考虑到将来的工作中可能不会再和串口打交道,这很可能是最后一版;
* \n 2009 v1.5 增加内置分块链表缓冲区; 增加对WINCE的支持(模拟器下测试通过);
* \n 2008 v1.4 增加对同步IO的多线程支持; 增加C++异常的支持; 改名ComComm; 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 COM_COMM_H
#pragma once
#define COM_COMM_H
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
//#include <windows.h>
#include <tchar.h>
#include <afxwin.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
#define ON_COM_CLOSE ON_COM_MSG_BASE + 14
#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
//#define CN_ASSERT(E) ((E) ? true: assert(E))
#ifndef CN_ASSERT
#define CN_2STR(L) _T(#L) //!< 将表达式L转换成字符串
#define CN_LINE(L) CN_2STR(L) //!< 将行号L转换成字符串
/*! 内部断言 启用异常将抛出异常 否则调试版将退出 发行版未启用异常将不做任何处理 */
//#define CN_ASSERT(E) ((E) ? true : ComComm::Assert(_T("CN_ASSERT(")_T(#E)_T(") failed; ComComm(")CN_LINE(__LINE__)_T("); ")))
#define CN_ASSERT(E) ((E) ? true : ComComm::Assert(_T("CN_ASSERT")))
#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
typedef void(__stdcall *ComCallback)(int nType, void *pThis);
class ComComm
{
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:默认打开串口时启动监视线程 启用写独立线程 启用写缓冲
ComComm(DWORD dwOption = EN_THREAD )
#else
//! WIN32:默认打开串口时启动监视线程 异步重叠方式
ComComm(DWORD dwOption = EN_THREAD | EN_OVERLAPPED)
#endif
{
Init();
SetOption(dwOption);
}
//! 另一模式构造 兼容cnComm1~1.3 \param[in] bThread 启动监视线程 \param[in] bOverlapped 启用重叠I/O
ComComm(bool bThread, bool bOverlapped)
{
DWORD dwOption = 0;
if (bThread)
dwOption |= EN_THREAD;
if (bOverlapped)
dwOption |= EN_OVERLAPPED;
Init();
SetOption(dwOption);
}
//! 析构 自动关闭串口
virtual ~ComComm()
{
Close();
Destroy();
}
//设置回调
void SetDataCallBack(ComCallback pCb,void *pUser)
{
m_pComCb = pCb;
m_pUser = pUser;
}
//设置回调
void SetDataCallBack(std::function<void()> _pfn)
{
m_pfn = _pfn;
}
//设置异步或者同步模式以及读数据阻塞时延,不设置即默认 异步,异步时读延时无效
void SetWorkMode(BOOL bAsync,DWORD lReadTimeout=250)
{
m_lReadTimeout = lReadTimeout;
if (!bAsync)
{
DWORD dwOption = EN_THREAD;
SetOption(dwOption);
}
else
{
DWORD dwOption = EN_THREAD | EN_OVERLAPPED;
SetOption(dwOption);
}
}
//! 判断串口是或打开
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_;
}
//! 获得ComComm的基本配置参数 返回32位配置掩码
DWORD GetOption()
{
return dwOption_;
}
//! 设置ComComm的基本配置参数 在打开串口前设置有意义 \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
}
//! 修改ComComm的基本配置参数 在打开串口前设置有意义 \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_OVERLAPPED掩码
dwOption_ &= (~EN_OVERLAPPED);
#endif
}
//! 是否重叠IO模式
bool IsOverlappedMode()
{
return dwOption_ & EN_OVERLAPPED ? true : false;
}
//! 是否输出缓冲区模式
bool IsTxBufferMode()
{
return dwOption_ & EN_TX_BUFFER ? true : false;
}
//! 是否输入缓冲区模式
bool IsRxBufferMode()
{
return dwOption_ & EN_RX_BUFFER ? true : false;
}
//! 设定发送通知, 接受字符最小值
void SetNotifyNum(DWORD dwNum)
{
dwNotifyNum_ = dwNum;
}
//! 获得线程句柄 \param[in] iOption EN_THREAD获得hWatchThread_ EN_RX_THREAD获得hReadThread_ EN_TX_THREAD获得hWriteThread_ \return HANDLE 线程句柄
HANDLE GetThread(int iOption = EN_THREAD)
{
return iOption == EN_THREAD ? hWatchThread_ : ((iOption == EN_RX_THREAD) ? hReadThread_ : hWriteThread_);
}
//! 设置要监视的事件, 打开前设置有效
void SetWaitEvent(DWORD dwEvent = CN_COMM_WAIT_EVENT)
{
CN_ASSERT(!IsOpen());///打开状态下不可以设置参数
dwWaitEvent_ = dwEvent;
}
//! 输入缓冲区
BlockBuffer& Input()
{
return I_;
}
//! 输出缓冲区
BlockBuffer& Output()
{
return O_;
}
//! 获得输入输出的计数 \param[in] bInput 为true获得输入计数,默认为true 重叠IO下输出是不精确的 因为输出由系统后台控制 假设完全输出
DWORD GetCounter(bool bInput = true)
{
return bInput ? dwInCount_ : dwOutCount_;
}
//! 输入输出计数器清零
void ResetCounter()
{
dwInCount_ = dwOutCount_ = 0;
}
//! 打开串口 请注意与cnComm1~1.3的区别 cnComm1~1.3将使用9600, n, 8, 1配置端口 而1.5将只打开端口不配置波特率等参数 \param[in] dwPort 串口序号 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 串口序号 1~1024 \param[in] dwBaudRate 波特率 \param[in] btParity 奇偶校验 \param[in] btByteSize 数据位数 \param[in] btStopBits 停止位数 */
//! 打开串口 缺省 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 串口序号 1~1024 \param[in] szSetStr 字符串参数 "BBBB,P,D,S" 例: "9600,N,8,1" "1200,O,7,2"
BBBB为波特率 P为奇偶校验(E | M | N | O | S) D为数据位数(4 ~ 8) S为停止位数(1 | 1.5 | 2)
\code Open(1, "9600,E,8,2"); \endcode \b 请注意字符串参数的顺序 并检查返回值
不支持WINCE, 原本我自己写了一个替代函数, 考虑到WINCE硬件的复杂性, 移植性可能不好, 故从这个版本中删除了 */
//! 打开串口, 字符串设置串口
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 此时该参数没有具体意义,仅用于消息通知中辨别端口号,应注意不和其他端口号重复
\param[in] szPortName 为指定的端口名称, 就像WINDOWS下\\\\.\\COM1或者WINCE下COM1: , 有些虚拟串口可以有特殊的名称
\param[in] dwBaudRate 波特率 \param[in] btParity 奇偶校验 \param[in] btByteSize 数据位数 \param[in] btStopBits 停止位数
\code Open(9999, "COM3:", 2400); \endcode \code Open(1028, "COM3:", 9600, N, 7, ONESTOPBIT); \endcode */
//! 指定的端口名称打开串口
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_s(szName_, szPortName);
dwPort_ = 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;
}
//! 直接读物理端口 \param[out] pBuffer 目标缓冲区 \param[in] dwLength 读取长度 \param[in] dwWaitTime 等待时间(默认INFINITE) \return 返回实际读取字节数
DWORD ReadPort(LPVOID pBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
{
if (!IsOpen())
{
return -1;
}
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())//! 重叠I/O下 dwWaitTime==INFINITE 将等待到自然超时或任务完成 具体时间由超时结构决定 默认设定0.5秒
{
if (dwWaitTime == INFINITE)//! 如果你没有启用读反冲 直接读取端口将导致阻塞 相当于原来1.3版本 Read(pBuffer, dwLength, false);
{
if (!::ReadFile(hComm_, pBuffer, dwLength, &dwReadResult, &RO_))
{//! 在界面里使用ComComm推荐使用读缓冲区或者在dwWaitTime指定超时时间
if (CN_ASSERT(::GetLastError() == ERROR_IO_PENDING))
{
while (!CN_ASSERT(::GetOverlappedResult(hComm_, &RO_, &dwReadResult, TRUE)))
;
}
}
}
else//! 重叠I/O下 dwWaitTime!=INFINITE 将等待到超时或任务完成 具体时间由dwWaitTime决定
{ //! dwWaitTime = 0 相当于原来1.3版本的功能
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
//! 阻塞I/O和WinCE的I/O下 dwWaitTime无意义 超时时间由超时结构决定 默认设定1/4秒
CN_ASSERT(::ReadFile(hComm_, pBuffer, dwLength, &dwReadResult, NULL));
return dwInCount_ += dwReadResult, dwReadResult;
}
//! 读取串口 dwLength个字符到 pBuffer 返回实际读到的字符数 可读任意数据
DWORD Read(LPVOID pBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
{
if (!IsOpen())
{
return -1;
}
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
}
//! 读取串口 dwLength - 1 个ANSI字符到 szBuffer 返回 C 模式字符串指针 适合一般字符通讯
char * ReadString(char *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
{
if (!IsOpen())
{
return "";
}
CN_ASSERT(szBuffer);
szBuffer[Read(szBuffer, dwLength - 1, dwWaitTime)] = '\0';
return szBuffer;
}
//! 读取串口 dwLength - 1 个UNICODE字符到 szBuffer 返回 C 模式字符串指针 适合一般字符通讯
wchar_t * ReadString(wchar_t *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
{
if (!IsOpen())
{
return L"";
}
CN_ASSERT(szBuffer);
szBuffer[(Read(szBuffer, (dwLength - 1)*sizeof(wchar_t), dwWaitTime) +1)/ sizeof(wchar_t)] = L'\0';
return szBuffer;
}
//! 直接写入端口
DWORD WritePort(LPCVOID pBuffer, DWORD dwLength)
{
if (!IsOpen())
{
return -1;
}
if(!CN_ASSERT(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())//! 使用重叠IO是返回0写出计数直接加上待写入长度即假设写入成功
{//! 重叠IO下超时由超时结构控制 默认为长度*1毫秒+10秒
if(!::WriteFile(hComm_, pBuffer, dwLength, &uWriteLength, &WO_)
&& !CN_ASSERT(::GetLastError() == ERROR_IO_PENDING))
uWriteLength = 0;
else
dwOutCount_ += dwLength;
}
else
#endif
{
//! 使用阻塞IO或WINCE下成功返回0 失败返回实际写入长度,写出计数直接加上实际写入长度, 超时默认1/4秒
BOOL bRet = ::WriteFile(hComm_, pBuffer, dwLength, &uWriteLength, NULL);
int error = ::GetLastError();
dwOutCount_ += uWriteLength;
uWriteLength = (dwLength == uWriteLength) ? uWriteLength : 0;
}
return uWriteLength;
}
//! 写 EN_TX_BUFFER 决定写入缓冲区还是直接写入端口
DWORD Write(LPCVOID pBuffer, DWORD dwLength)
{
if (!IsOpen())
{
return -1;
}
if (dwLength && (dwOption_&EN_TX_BUFFER))
{
BlockBuffer::InnerLock locker(&O_);
O_.Write(pBuffer, dwLength);
if (dwOption_ & EN_TX_THREAD)
SetEvent(hWatchEvent_);
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
}
//! 写串口 ANSI字符 写ANSI C 模式字符串指针
DWORD Write(const char *szBuffer,int nBufferLen)
{
if (!IsOpen())
{
return -1;
}
CN_ASSERT(szBuffer);
return Write((LPCVOID)szBuffer, nBufferLen);
}
//! 强制输出队列内数据并等待
void FlushPort()
{
if(CN_ASSERT(IsOpen()))
FlushFileBuffers(hComm_);
}
//! 强制输出写缓冲区并强制输出队列内数据并等待
void Flush()
{
if(CN_ASSERT(IsOpen()))
{
if (dwOption_ & EN_TX_BUFFER)
{
while(O_.SafeSize())
Sleep(50);
}
FlushFileBuffers(hComm_);
}
}
//! 抢先输出字符
bool TransmitChar(char c)
{
if (CN_ASSERT(IsOpen()))
{
if (::TransmitCommChar(hComm_, c))
return dwOutCount_++, true;
}
return false;
}
//! 启动辅助线程控制
bool BeginThread(DWORD dwThreadOption = 0)
{
DWORD dwCreationFlags;
if(CN_ASSERT(!hWatchThread_ && !hReadThread_ && !hWriteThread_))
{//! 必须是没有线程在运行状态下才可以启动线程
bContinue_ = true;
dwCreationFlags = dwOption_ & EN_SUSPEND ? CREATE_SUSPENDED : 0;
if (dwThreadOption)//! 根据配置创建监视线程,独立读或写线程
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;
}
//! 暂停线程
bool SuspendThread(int iOption = EN_THREAD)
{
return ::SuspendThread(GetThread(iOption)) != 0xFFFFFFFF;
}
//! 恢复线程
bool ResumeThread(int iOption = EN_THREAD)
{
return ::ResumeThread(GetThread(iOption)) != 0xFFFFFFFF;
}
//! 终止线程
bool EndThread(DWORD dwWaitTime = 500)
{
if(hWatchThread_ || hReadThread_ || hWriteThread_)
{
if ((dwOption_&EN_FLUSH_ALL) && (dwOption_&EN_TX_BUFFER))
{//! 如果启用EN_FLUSH_ALL将循环等待写缓冲区清空如果写入线程不能正常工作将挂起
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;
}
//! 关闭串口 同时也关闭关联线程
virtual void Close(DWORD dwWaitTime = 500)
{
if(IsOpen())
{
EndThread(dwWaitTime);//! 同步结束线程
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;
}
}
//! 锁定
void Lock()
{
::EnterCriticalSection(&CS_);
}
//! 解锁
void Unlock()
{
::LeaveCriticalSection(&CS_);
}
//! 自动锁 用于函数内部 利用对象的生命周期完成锁定及解锁
struct InnerLock
{
ComComm* ptr;//!< ComComm 对象指针
//! 锁定
InnerLock(ComComm* p) : ptr(p)
{
ptr->Lock();
}
//! 解锁
~InnerLock()
{
ptr->Unlock();
}
};
//! 获得串口参数 DCB
DCB *GetState(DCB *pDcb = NULL)
{
return CN_ASSERT(IsOpen()) && ::GetCommState(hComm_, pDcb == NULL ? &DCB_ : pDcb) == TRUE ? (pDcb == NULL ? &DCB_ : pDcb) : NULL;
}
//! 设置串口参数 DCB
bool SetState(DCB *pDcb = NULL)
{
return CN_ASSERT(IsOpen()) ? ::SetCommState(hComm_, pDcb == NULL ? &DCB_ : pDcb) == TRUE : false;
}
//! 设置串口参数:波特率,停止位,等 ***
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
//! 设置串口参数 支持设置字符串 "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);
BOOL bRet = ::SetCommState(hComm_, &DCB_) == TRUE;
bool smallBool = false;
bRet == 0 ? smallBool = false : smallBool = true;
return smallBool;
}
return false;
}
#endif
//! 获得超时结构
LPCOMMTIMEOUTS GetTimeouts(LPCOMMTIMEOUTS lpCO = NULL)
{
return CN_ASSERT(IsOpen()) && ::GetCommTimeouts(hComm_, lpCO ? lpCO : &CO_) == TRUE ? (lpCO ? lpCO : &CO_) : NULL;
}
//! 设置超时
bool SetTimeouts(LPCOMMTIMEOUTS lpCO = NULL)
{
return CN_ASSERT(IsOpen()) ? ::SetCommTimeouts(hComm_, lpCO ? lpCO : &CO_) == TRUE : false;
}
//! 设置串口的I/O缓冲区大小
bool Setup(DWORD dwInputSize = 4096, DWORD dwOutputSize = 4096)
{
return CN_ASSERT(IsOpen()) ? ::SetupComm(hComm_, dwInputSize, dwOutputSize) == TRUE : false;
}
//! 调整端口功能
bool Escape(DWORD dwType)
{
return CN_ASSERT(IsOpen()) ? EscapeCommFunction(hComm_, dwType) != 0 : false;
}
//! 获得调制解调器相关信号状态
DWORD GetModemStatus()
{
DWORD dwModemStat = 0;
return CN_ASSERT(IsOpen()) && GetCommModemStatus(hComm_, &dwModemStat) ? dwModemStat : 0;
}
//! 获得端口参数 \param[in] pCP 结构指针 如果pCP==NULL, ComComm将从堆分配内存, 并由ComComm负责释放, 用户不需要自己释放内存
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;
}
//! 获取事件标识
DWORD GetMask()
{
DWORD dwMask;
return CN_ASSERT(IsOpen()) && GetCommMask(hComm_, &dwMask) ? dwMask : 0;
}
//! 清除端口缓冲区
bool Purge(DWORD dwPara = PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR)
{
return CN_ASSERT(IsOpen()) ? ::PurgeComm(hComm_, dwPara)==TRUE : false;
}
//! 获得错误代码
DWORD ClearError()
{
DWORD dwError;
return CN_ASSERT(IsOpen()) && ::ClearCommError(hComm_, &dwError, NULL) ? dwError : 0;
}
//! 获得读物理缓冲区的字符数
DWORD GetQueueCount(bool bInput = true)
{
COMSTAT Stat;
DWORD dwError;
return CN_ASSERT(IsOpen()) && ::ClearCommError(hComm_, &dwError, &Stat) ? (bInput ? Stat.cbInQue : Stat.cbOutQue) : (DWORD)-1L;
}
//! 调制解调器 CTS ON
bool CheckCTS()
{
return (GetModemStatus()&MS_CTS_ON) != 0;
}
//! 调制解调器 DSR ON
bool CheckDSR()
{
return (GetModemStatus()&MS_DSR_ON) != 0;
}
//! 调制解调器 Ring ON
bool CheckRING()
{
return (GetModemStatus()&MS_RING_ON) != 0;
}
//! 调制解调器 RLSD ON
bool CheckRLSD()
{
return (GetModemStatus()&MS_RLSD_ON) != 0;
}
//! DTR 电平控制
bool SetDTR(bool bSetOrClr = true)
{
return Escape(bSetOrClr ? SETDTR : CLRDTR);
}
//! RTS 电平控制
bool SetRTS(bool bSetOrClr = true)
{
return Escape(bSetOrClr ? SETRTS : CLRRTS);
}
//! 调制解调器
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
//! 流控制 SETXON SETXOFF
bool SetX(bool bOnOrOff)
{
return Escape(bOnOrOff ? SETXON : SETXOFF);
}
//! 根据通讯特点开发的缓冲区类 单向链表内存块 有一些扩展以支持和API挂接
class BlockBuffer
{
public:
//! 缓冲区内存块
struct Block
{
DWORD B_; //!< 开始偏移
DWORD E_; //!< 结束偏移
DWORD S_; //!< 块大小 内存块最大值不限 内存块最小值由CN_COMM_BUFFER_MIN_BLOCK_SIZE决定
Block* N_; //!< 下一个块指针
BYTE P_[4]; //!< 缓冲区指针 实际大小由S_决定
//! 容量
DWORD Capacity() { return S_; }
//! 实际大小
DWORD Size() { return E_ - B_; }
//! 开始缓冲区指针
BYTE* Begin() { return P_ + B_; }
//! 末端缓冲区指针
BYTE* End() { return P_ + E_; }
//! 下一个块
Block* Next() { return N_; }
//! 是否空
bool IsEmpty() { return B_ == E_; }
//! 空闲大小
DWORD FreeSize() { return S_ - E_; }
};
//! 用于缓冲区单向迭代子
struct Iterator
{
BlockBuffer* P_; //!< 对象指针
Block* B_; //!< 当前块指针
DWORD I_; //!< 当前块偏移
DWORD O_; //!< 全缓冲区偏移
//! 构造
Iterator(BlockBuffer *P = NULL, Block* B = NULL, DWORD I = 0, DWORD O = 0)
: P_(P), B_(B), I_(I), O_(O)
{
}
//! 是否有效
operator bool()
{
return B_ && I_<B_->E_&&I_>=B_->B_;
}
//! 是否可写
bool CanWrite()
{
return B_ && I_ < B_->S_;
}
//! 取值引用
BYTE& operator *()
{
CN_ASSERT( P_ && B_ && I_<B_->E_&&I_>=B_->B_);
return B_->P_[I_];
}
//! 移动迭代子 ++Iter;
Iterator& operator ++ ()
{
return operator +=(1);
}
//! 移动迭代子 Iter++;
Iterator& operator ++ (int)
{
return operator +=(1);
}
//! 移动迭代子 \param dwOffset 为偏移量
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;
}
//! 比较
bool operator == (const Iterator& iter)
{
return (P_ == iter.P_) && (B_ == iter.B_) && (I_ == iter.I_);
}
};
//! 友元
friend struct Iterator;
//! 锁定
void Lock()
{
::EnterCriticalSection(&C_);
}
//! 解锁
void Unlock()
{
::LeaveCriticalSection(&C_);
}
//! 自动锁
struct InnerLock
{
BlockBuffer* ptr;//!<对象指针
///锁定
InnerLock(BlockBuffer* p) : ptr(p)
{
if (ptr)
ptr->Lock();
}
///解锁
~InnerLock()
{
if (ptr)
ptr->Unlock();
}
};
//! 构造
BlockBuffer()
{
::InitializeCriticalSection(&C_);
S_ = 0, F_ = L_ = NULL, M_ = CN_COMM_BUFFER_MIN_BLOCK_SIZE;
}
//! 析构自动释放空间
virtual ~BlockBuffer()
{
Clear();
::DeleteCriticalSection(&C_);
}
//! 获得起始迭代子
Iterator Begin()
{
return Iterator(this, F_, F_? F_->B_ : 0, 0);
}
//! 设置块的最小长度
void SetMinBlockSize(DWORD dwMinSize)
{
M_ = dwMinSize;
}
//! 获得块的最小长度
DWORD GetMinBlockSize()
{
return M_;
}
//! 缓冲区内数据字节数
DWORD Size()
{
return S_;
}
//! 缓冲区大小
DWORD SafeSize()
{
InnerLock lock(this);
return S_;
}
//! 写入ANSI字符串缓冲区 \sa Write(LPCVOID lpBuf, DWORD dwSize)
DWORD Write(const char* lpBuf)
{
return Write(lpBuf, strlen(lpBuf));
}
//! 写入UNICODE字符串缓冲区 \sa Write(LPCVOID lpBuf, DWORD dwSize)
DWORD Write(const wchar_t* lpBuf)
{
return Write(lpBuf, wcslen(lpBuf)*sizeof(wchar_t));
}
//! 写入缓冲区 \param[out] lpBuf 目标缓冲区 \param[in] dwSize 数据字节数 \return 实际复制数据字节数
DWORD Write(LPCVOID lpBuf, DWORD dwSize)
{
DWORD dwTemp = dwSize, dwFree = FreeSize(), dwCopy = 0;
if (dwFree)//! 首先查找末尾空闲,并写入数据
{
dwCopy = dwFree > dwSize ? dwSize : dwFree;
memcpy(L_->P_ + L_->E_, lpBuf, dwCopy);
dwTemp -= dwCopy, L_->E_ += dwCopy;
}
if (dwTemp)//! 剩余的数据分配新的空间并写入
{
memcpy(NewBlock(dwSize)->P_, ((LPBYTE)lpBuf )+ dwCopy, dwTemp);
L_->E_ += dwTemp;
}
S_ += dwSize;
return dwSize;
}
//! 线程安全写入缓冲区 \sa Write(LPCVOID lpBuf, DWORD dwSize)
DWORD SafeWrite(LPCVOID lpBuf, DWORD dwSize)
{
InnerLock lock(this);
return Write(lpBuf, dwSize);
}
//! 线程安全写入ANSI字符串缓冲区 \sa Write(LPCVOID lpBuf, DWORD dwSize)
DWORD SafeWrite(const char* lpBuf)
{
InnerLock lock(this);
return Write(lpBuf, strlen(lpBuf));
}
//! 线程安全写入UNICODE字符串缓冲区 \sa Write(LPCVOID lpBuf, DWORD dwSize)
DWORD SafeWrite(const wchar_t* lpBuf)
{
InnerLock lock(this);
return Write(lpBuf, wcslen(lpBuf)*sizeof(wchar_t));
}
//! 复制数据 \param[out] lpBuf 目标缓冲区 \param[in] dwSize 数据字节数 \param[in] dwStart 源缓冲区开始偏移值 \return 实际复制数据字节数
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;
}
//! 线程安全复制数据 \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 目标缓冲区 为NULL时仅删除不复制数据 \param[in] dwSize 操作数据字节数 \return 实际复制或删除字节数 */
//! 复制并删除缓冲区的数据到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_)
{//! 删除回收完全空闲块, 当一般保留1个块, 在小于4*CN_COMM_BUFFER_MIN_BLOCK_SIZE的情况下
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;
}
//! 读入ANSI字符串缓冲区
char* ReadString(char* lpBuf, DWORD dMaxSize)
{
lpBuf[Read(lpBuf, dMaxSize)] = '\0';
return lpBuf;
}
//! 读入UNICODE字符串缓冲区
wchar_t* ReadString(wchar_t* lpBuf, DWORD dMaxSize)
{
lpBuf[(Read(lpBuf, dMaxSize*sizeof(wchar_t))+1) / sizeof(wchar_t)] = L'\0';
return lpBuf;
}
//! 线程安全的读函数
DWORD SafeRead(LPVOID lpBuf, DWORD dwSize)
{
InnerLock lock(this);
return Read(lpBuf, dwSize);
}
//! 线程安全读入ANSI字符串缓冲区
char* SafeReadString(char* lpBuf, DWORD dMaxSize)
{
InnerLock lock(this);
return ReadString(lpBuf, dMaxSize);
}
//! 线程安全读入UNICODE字符串缓冲区
wchar_t* SafeReadString(wchar_t* lpBuf, DWORD dMaxSize)
{
InnerLock lock(this);
return ReadString(lpBuf, dMaxSize);
}
//! 清除 \param bDeleteAll 为true时释放所有内存, 否则保留一个内存块以提高效率
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;
}
}
//! 线程安全的清除函数 \sa void Clear(bool bDeleteAll = false)
void SafeClear(bool bDeleteAll = false)
{
InnerLock lock(this);
Clear(bDeleteAll);
}
//! 获得内存块指针 \param bFirst 为true时获得链表头部指针否则获得链表尾部指针
Block* GetBlockPtr(bool bFirst = true)
{
return bFirst ? F_ : L_;
}
//! 缓冲区尾部的空闲空间
DWORD FreeSize()
{
return L_ ? L_->S_-L_->E_ : 0 ;
}
//! 获得供API插入数据的直写缓冲区 传入所需大小dwSize 空闲不足分配新块 传入0返回尾部的空闲指针 尾部无空闲返回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 传入增量并非全量
\code strcpy(GetFreePtr(100), "test"), Release(4); //获取100字节的缓冲区 拷入四个字节 调整大小增加4个字节 \endcode
*/
//! 利用API直接写入用GetFreePtr()获得空闲指针, 同步调整缓冲区大小
DWORD Release(DWORD dwSize)
{
return (dwSize <= L_->S_) ? (L_->E_ += dwSize, S_ += dwSize, dwSize) : 0;
}
//! 获得第一个有效块的缓冲区指针
LPVOID GetPtr()
{
return F_ ? F_->P_ + F_->B_ : NULL;
}
//! 获得第一个有效块的缓冲区指针指向的数据大小
DWORD GetPtrSize()
{
return F_ ? F_->E_ - F_->B_ : 0;
}
//! 数组访问 如果块数超过1 效率很低 推荐用迭代子访问可以获得理想性能
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:
//! 新建块 自动添加在尾部
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_;//!< 头指针
Block* L_;//!< 尾指针
DWORD S_;//!< 大小
DWORD M_;//!< 块最小长度
CRITICAL_SECTION C_;//!< 锁结构
};
#if defined(CN_COMM_MFC_EXCEPTION)
/*! 需要定义宏 CN_COMM_MFC_EXCEPTION CN_ASSERT \n 将使用throw new MfcException(msg);语句抛出MFC异常(VC++) */
//! 用于MFC的异常
class MfcException : public CException
{
public:
//! 构造函数要求CException自动析构
MfcException(LPCTSTR szMsg) : CException(TRUE)
{
lstrcpy(szMsg_, szMsg);
#ifdef _DEBUG
CException::m_bReadyForDelete = TRUE;
#endif
}
//! 错误提示信息
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]; //!< 错误信息 格式CN_ASSERT(表达式) failed; ComComm(行号); Code(GetLastError()); 错误代码描述
};
#endif
//! 用户启用异常 则抛出异常; 未启用异常: DEBUG版本 控制台版本输出错误信息 并退出; Release版本弹出提示框并响应用户要求
static bool Assert(LPCTSTR szMessage)
{
return false;
//#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_s(szMsg, szMessage);
// dwError = GetLastError();
// if (dwError)//! 错误代码(GetLastError())不为 0 输出错误描述
// {
// dwLength = _tcslen(szMsg);
// _sntprintf_s(szMsg + dwLength, 256 - _tcslen(szMsg), 256, _T("Code:%d; "), dwError);
// dwLength = _tcslen(szMsg);
// FormatMessage(
// FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
// NULL,
// dwError,
// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //! 错误描述采用本地语言
// szMsg + dwLength - 1,
// 256 - dwLength - 1,
// NULL
// );
// }
//#else
// ;// Release版本 什么都不做 预留
//#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
// // 需要 setlocale(LC_ALL, "chs"); 控制台才可以正确输出UNICODE中文 这里转换ANSI 避免这样问题
// #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版本 什么都不做 预留
//#endif
// return false;
}
protected:
DWORD m_lReadTimeout;
ComCallback m_pComCb; //!< 数据回调
void* m_pUser; //!< 数据对象
std::function<void()> m_pfn;
DWORD dwOption_; //!< 关键模式配置 在构造函数中设定
HANDLE hComm_; //!< 串口句柄
DWORD dwPort_; //!< 串口号 0为特殊串口 -1为无效 主要终于消息通知甄别
TCHAR szName_[64]; //!< 保存串口名称 类似COM1的字符串
CRITICAL_SECTION CS_; //!< 临界互斥锁
DCB DCB_; //!< 波特率,停止位,等
COMMTIMEOUTS CO_; //!< 超时结构
COMMPROP* pCP_; //!< 串口参数
BlockBuffer I_; //!< 读缓冲区
BlockBuffer O_; //!< 写缓冲区
DWORD dwInCount_; //!< 读计数
DWORD dwOutCount_; //!< 写计数
bool bContinue_; //!< 线程继续运行循环标志
DWORD dwWaitEvent_; //!< WaitCommEvent 的监视事件
HANDLE hWatchThread_; //!< 监视辅助线程
DWORD hWatchThreadId_; //!< 监视辅助线程 ID
HANDLE hWatchEvent_; //!< 通知监视线程
HANDLE hReadThread_; //!< 接收辅助线程 实现双工提高效率
DWORD hReadThreadId_; //!< 接收辅助线程 ID
HANDLE hReadEvent_; //!< 通知接收线程
HANDLE hWriteThread_; //!< 发送辅助线程 实现双工提高效率
DWORD hWriteThreadId_; //!< 发送辅助线程 ID
HANDLE hWriteEvent_; //!< 通知发送线程
DWORD dwNotifyNum_; //!< 接受多少字节(>=dwNotifyNum_)发送通知消息
#ifndef CN_COMM_FOR_CE
OVERLAPPED RO_; //!< 重叠I/O ReadFile
OVERLAPPED WO_; //!< 重叠I/O WriteFile
OVERLAPPED EO_; //!< 重叠I/O WaitCommEvent
#endif
//! 初始化
virtual void Init()
{
::InitializeCriticalSection(&CS_);
memset(szName_, 0, 64*sizeof(TCHAR));
memset(&DCB_, 0, sizeof(DCB_));
DCB_.DCBlength = sizeof(DCB_);
m_pComCb = NULL;
m_pUser = NULL;
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;
dwNotifyNum_ = 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);
m_lReadTimeout = 250;//250ms
}
//! 析构
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_);
}
//! 绑定串口
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
}
//! 打开串口
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();
}
//! 设置串口
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)))//! 配置端口发送接收队列大小, 读4096字节, 写4096字节, 阻塞I/O模式发送队列无意义
return false;
#endif
if(!CN_ASSERT(::GetCommTimeouts(hComm_, &CO_)))
return false;
CO_.ReadIntervalTimeout = 50;//! 配置超时结构 字符最小间隔100ms
CO_.ReadTotalTimeoutMultiplier = 0;
CO_.ReadTotalTimeoutConstant = IsOverlappedMode() ? 500 : m_lReadTimeout;//! 读超时 重叠I/O模式下500毫秒 阻塞I/O模式下250毫秒
CO_.WriteTotalTimeoutMultiplier = IsOverlappedMode() ? 1 : 0;
CO_.WriteTotalTimeoutConstant = IsOverlappedMode() ? 10000 : 250;//! 写超时 重叠I/O模式下(10000+1×字节数)毫秒 阻塞I/O模式下250毫秒
if(!CN_ASSERT(::SetCommTimeouts(hComm_, &CO_)))
return false;
if(!CN_ASSERT(::PurgeComm(hComm_, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR )))//! 清除端口
return false;
return true;
}
//! 将端口数据读入缓冲区的
DWORD PortToBuffer(DWORD dwPortByteNum)
{
BlockBuffer::InnerLock locker(&I_);
return dwPortByteNum ? I_.Release(ReadPort(I_.GetFreePtr(dwPortByteNum), dwPortByteNum)) : 0;
}
//! 将缓冲区的数据写入端口
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 消息 \param lParam 参数 \param bPostMode 采用PostMessage或者SendMessage发送消息*/
//! 当窗口句柄或线程ID有效, 送出消息, 用于相应事件通知, WPARAM默认包含串口编号
virtual void Notify(UINT uMsg, LPARAM lParam = 0, bool bPostMode = true)
{
if (NULL != m_pComCb)
m_pComCb(uMsg, m_pUser);
else if (m_pfn != nullptr)
m_pfn();
}
//---------------------------------------threads callback-----------------------------------------------------
//! 响应EV_RXCHAR事件 由线程回调, 虚函数可以在基层类中扩展(推荐方式)
virtual void OnReceive()
{
Notify(ON_COM_RECEIVE);
}
//! 响应EV_DSR事件
virtual void OnDSR()
{
Notify(ON_COM_DSR, CheckDSR() ? 1 : 0);
}
//! 响应EV_CTS事件
virtual void OnCTS()
{
Notify(ON_COM_CTS, CheckCTS() ? 1 : 0);
}
//! 响应EV_BREAK事件
virtual void OnBreak()
{
ClearCommBreak(hComm_);
Notify(ON_COM_BREAK);
}
//! 响应EV_TXEMPTY事件
virtual void OnTxEmpty()
{
Notify(ON_COM_TXEMPTY);
}
//! 响应EV_ERROR事件
virtual void OnError()
{
Notify(ON_COM_ERROR, ClearError());
}
//! 响应EV_RING事件
virtual void OnRing()
{
Notify(ON_COM_RING, CheckRING() ? 1 : 0);
}
//! 响应EV_RLSD事件
virtual void OnRLSD()
{
Notify(MS_RLSD_ON, CheckRLSD() ? 1 : 0);
}
//! 响应EV_RXFLAG事件
virtual void OnRxFlag()
{
Notify(ON_COM_RXFLAG);
}
//! 响应EV_POWER事件
virtual void OnPower()
{
Notify(ON_COM_POWER);
}
//! 响应EV_RX80FULL事件
virtual void OnRx80Full()
{
Notify(ON_COM_RX80FULL);
}
//! 响应EV_EVENT1事件
virtual void OnEvent1()
{
Notify(ON_COM_EVENT1);
}
//! 响应EV_EVENT2事件
virtual void OnEvent2()
{
Notify(ON_COM_EVENT2);
}
//! 响应EV_PERR事件
virtual void OnPrintErr()
{
Notify(ON_COM_PERR);
}
//! 通用的事件处理
virtual void HandleEvent(DWORD dwMask)
{
if ((dwMask & EV_RXCHAR) && !(dwOption_&EN_RX_THREAD))
{
DWORD dwLength = GetQueueCount(true);
if (dwLength)
{
if (dwOption_ & EN_RX_BUFFER)
{
PortToBuffer((dwLength << 1) + 64);//多出的字节数 用来避免事件间隙的陷阱
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
//! 监视线程用于重叠I/O模型
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))//! 等待事件前会先扫描队列是否还有未处理数据 如果有模拟一个EV_RXCHAR事件 避免事件通知的陷阱
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
//! 监视线程用于阻塞I/O模型
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())
BufferToPort();
ResetEvent(hWatchEvent_);
break;
case WAIT_TIMEOUT:
DWORD dwLength = GetQueueCount(true);
if (dwLength)
{
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
//! 监视线程用于WINCE的I/O模型(重叠+阻塞 也不知微软把它叫什么模式)
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))//! 等待事件前会先扫描队列是否还有未处理数据 如果有模拟一个EV_RXCHAR事件 避免事件通知的陷阱
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
//! 用于双工处理当然您可以改变用途
virtual DWORD ReadModel()
{
while(bContinue_)
{
Sleep(50);
DWORD dwLength = GetQueueCount(true);
if (dwLength)
{
if (dwOption_ & EN_RX_BUFFER)
{
PortToBuffer(dwLength);
if (I_.Size() >= dwNotifyNum_)
OnReceive();
}
else
{
if (dwLength >= dwNotifyNum_)
OnReceive();
}
}
}
return 0;
}
//! 用于双工处理当然您可以改变用途
virtual DWORD WriteModel()
{
if (!IsOpen())
{
return -1;
}
CN_ASSERT(dwOption_ & EN_TX_BUFFER);
while (bContinue_)
{
DWORD dwReturn = ::WaitForSingleObject(hWriteEvent_, 200);
while(bContinue_ && O_.SafeSize())
BufferToPort();
if (dwReturn == WAIT_OBJECT_0)
ResetEvent(hWatchEvent_);
}
return 0;
}
private:
//! 禁止拷贝构造
ComComm(const ComComm&);
//! 禁止赋值函数
ComComm &operator = (const ComComm&);
#if defined(_MT) && !defined(CN_COMM_FOR_CE)
//! 监视线程 Watch MyThread 完成监视 并根据配置决定是否完成读写
static UINT APIENTRY WatchThreadProc(LPVOID lpPara)
#else
//! 监视线程 Watch MyThread 完成监视 并根据配置决定是否完成读写
static DWORD WINAPI WatchThreadProc(LPVOID lpPara)
#endif
{
#ifdef CN_COMM_FOR_CE
DWORD dwCode = ((ComComm *)lpPara)->EmbeddedModel();
#else
DWORD dwCode = ((ComComm *)lpPara)->IsOverlappedMode()
? ((ComComm *)lpPara)->OverlappedModel()
: ((ComComm *)lpPara)->NonoverlappedModel();
#if defined(_MT)
_endthreadex(dwCode);
#endif
#endif
return dwCode;
}
#if defined(_MT) && !defined(CN_COMM_FOR_CE)
//! 双工的线程 暂时未用 估计用不着
static UINT APIENTRY ReadThreadProc(LPVOID lpPara)
#else
//! 双工的线程 暂时未用 估计用不着
static DWORD WINAPI ReadThreadProc(LPVOID lpPara)
#endif
{
DWORD dwCode = ((ComComm *)lpPara)->ReadModel();
#if defined(_MT) && !defined(CN_COMM_FOR_CE)
_endthreadex(dwCode);
#endif
return dwCode;
}
#if defined(_MT) && !defined(CN_COMM_FOR_CE)
//! 双工的线程 WINCE可以采用
static UINT APIENTRY WriteThreadProc(LPVOID lpPara)
#else
//! 双工的线程 WINCE可以采用
static DWORD WINAPI WriteThreadProc(LPVOID lpPara)
#endif
{
DWORD dwCode = ((ComComm *)lpPara)->WriteModel();
#if defined(_MT) && !defined(CN_COMM_FOR_CE)
_endthreadex(dwCode);
#endif
return dwCode;
}
};
#endif //! _CN_COMM_H_