-
分享一个简洁实用的C++串口类CxComm
添加时间:2013-7-6 点击量:这是我在实际工作中设计的一个C++串口类,类名、函数名和变量名均采取匈牙利定名法。小写的x代表我的姓氏首字母(谢欣能),小我习惯罢了,如有类似,纯属偶合。
CxComm的定义如下:
#define MAX_COMM_BUFFER 4096
#define DEF_COMM_INPUT_BUFFER 8192
class XIO_CLASS CxComm
{
public:
CxComm();
virtual ~CxComm();
public:
BOOL Open(LPCSTR lpszPort);
BOOL Listen();
BOOL Close();
void SetWindowMessage(MSG pMsg, WNDPROC wndproc = NULL)
{
m_msg = pMsg;
m_wndproc = wndproc;
}
HANDLE GetSafeHandle() { return m_hComm; }
void GetCommPort(LPSTR lpszPort) { strcpy(lpszPort, m_szComm); }
void SetRThreshold(BYTE btRThreshold) { m_btRThreshold = btRThreshold; }
BYTE GetRThreshold() { return m_btRThreshold; }
BOOL GetSettings(LPDCB lpdcb);
BOOL SetSettings(LPDCB lpdcb);
BOOL GetBufferSize(LPDWORD lpdwInSize, LPDWORD lpdwOutSize);
BOOL SetBufferSize(DWORD dwInSize, DWORD dwOutSize);
BOOL GetBufferCount(LPDWORD lpdwInSize, LPDWORD lpdwOutSize);
BOOL ClearBuffer(BOOL bInput);
BOOL Receive(LPBYTE lpbtData, DWORD dwSize);
BOOL Send(LPBYTE lpbtData, DWORD dwSize);
void SelectEvent(long lEvent) { m_dwEvent = lEvent; }
protected:
void DisListen();
static DWORD WINAPI ListenProc(LPVOID lpParam);
void OnListen();
private:
char m_szComm[FIX_SERIALCOMM];
HANDLE m_hComm;
HANDLE m_hThreadListen, m_hEventListen;
BOOL m_bExitThreadListen;
BYTE m_btRThreshold;
DWORD m_dwInBuffSize, m_dwOutBuffSize, m_dwEvent;
MSG m_msg;
WNDPROC m_wndproc;
};因为这个类被我封装在动态库里面,所以类名前应用了导出标记XIO_CLASS,读者在应用时完全可以去掉。别的,FIX_SERIALCOMM是一个定义为8的宏,来自对另一个头文件的引用(将来的文章会向大师介绍)。这个类的定义被我放在一个包含很多类定义的头文件中,没有零丁为它写头文件,所以它的定义项目组代码看上去没有高低文。
CxComm的实现如下:
// xComm.cpp: implementation of the CxComm class.
//
//////////////////////////////////////////////////////////////////////
#include stdafx.h
#include ../xio.h
#include <stdio.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CxComm::CxComm()
: m_hComm(NULL)
, m_hThreadListen(NULL)
, m_hEventListen(NULL)
, m_dwEvent(EV_RXCHAR)
, m_bExitThreadListen(TRUE)
, m_btRThreshold(1)
, m_dwInBuffSize(MAX_COMM_BUFFER)
, m_dwOutBuffSize(MAX_COMM_BUFFER)
, m_wndproc(NULL)
{
ZeroMemory(m_szComm, sizeof(m_szComm));
ZeroMemory(&m_msg, sizeof(m_msg));
}
CxComm::~CxComm()
{
}
BOOL CxComm::Open(LPCSTR lpszPort)
{
if ((lpszPort == NULL) || (lpszPort[0] == \0))
return FALSE;
Close();
HANDLE hCom = CreateFile(lpszPort, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hCom == INVALID_HANDLE_VALUE)
return FALSE;
strcpy(m_szComm, lpszPort);
m_hComm = hCom;
DWORD dwError;
COMSTAT cs = {0};
ClearCommError(m_hComm, &dwError, &cs);
return TRUE;
}
BOOL CxComm::Listen()
{
if (m_hComm == NULL)
return FALSE;
if (m_hThreadListen != NULL)
return FALSE;
SetCommMask(m_hComm, m_dwEvent);
m_bExitThreadListen = FALSE;
DWORD dwExitCode;
m_hThreadListen = CreateThread(NULL, 0, ListenProc, this, 0, &dwExitCode);
return (m_hThreadListen != NULL);
}
void CxComm::DisListen()
{
if (m_hThreadListen != NULL)
{
m_bExitThreadListen = TRUE;
SetEvent(m_hEventListen);
WaitForSingleObject(m_hThreadListen, INFINITE);
CloseHandle(m_hThreadListen);
m_hThreadListen = NULL;
}
}
BOOL CxComm::Close()
{
BOOL bRet = TRUE;
if (m_hComm != NULL)
{
DisListen();
ClearBuffer(TRUE);
ClearBuffer(FALSE);
CloseHandle(m_hComm);
m_hComm = NULL;
ZeroMemory(m_szComm, sizeof(m_szComm));
}
m_wndproc = NULL;
return bRet;
}
BOOL CxComm::GetSettings(LPDCB lpdcb)
{
if (m_hComm == NULL)
return FALSE;
return GetCommState(m_hComm, lpdcb);
}
BOOL CxComm::SetSettings(LPDCB lpdcb)
{
if (m_hComm == NULL)
return FALSE;
return SetCommState(m_hComm, lpdcb);
}
BOOL CxComm::GetBufferSize(LPDWORD lpdwInSize, LPDWORD lpdwOutSize)
{
if (m_hComm == NULL)
return FALSE;
COMMPROP cp = {0};
if (!GetCommProperties(m_hComm, &cp))
return FALSE;
if (lpdwInSize != NULL)
lpdwInSize = cp.dwCurrentRxQueue;
if (lpdwOutSize != NULL)
lpdwOutSize = cp.dwCurrentTxQueue;
return TRUE;
}
BOOL CxComm::SetBufferSize(DWORD dwInSize, DWORD dwOutSize)
{
if (m_hComm == NULL)
return FALSE;
return SetupComm(m_hComm, dwInSize, dwOutSize);
}
BOOL CxComm::GetBufferCount(LPDWORD lpdwInSize, LPDWORD lpdwOutSize)
{
if (m_hComm == NULL)
return FALSE;
DWORD dwError;
COMSTAT cs = {0};
if (!ClearCommError(m_hComm, &dwError, &cs))
return FALSE;
if (lpdwInSize != NULL)
lpdwInSize = cs.cbInQue;
if (lpdwOutSize != NULL)
lpdwOutSize = cs.cbOutQue;
return TRUE;
}
BOOL CxComm::ClearBuffer(BOOL bInput)
{
if (m_hComm == NULL)
return FALSE;
DWORD dwFlag = bInput ? PURGE_RXCLEAR : PURGE_TXCLEAR;
return PurgeComm(m_hComm, dwFlag);
}
BOOL CxComm::Send(LPBYTE lpbtData, DWORD dwSize)
{
if (m_hComm == NULL)
return FALSE;
DWORD dwWritten = 0, dwToWrite, dwRet;
BOOL bRet;
OVERLAPPED o = {0};
char szEvent[MAX_EVENT];
sprintf(szEvent, %d.%s.Send, (DWORD)this, m_szComm);
// asynchronous
while (dwWritten != dwSize)
{
dwToWrite = dwSize - dwWritten;
o.hEvent = CreateEvent(NULL, TRUE, FALSE, szEvent);
bRet = WriteFile(m_hComm, &lpbtData[dwWritten], dwToWrite, NULL, &o);
if (!bRet)
{
dwRet = GetLastError();
if (ERROR_IO_PENDING == dwRet)
WaitForSingleObject(o.hEvent, INFINITE);
}
CloseHandle(o.hEvent);
dwWritten += o.InternalHigh;
}
return TRUE;
}
BOOL CxComm::Receive(LPBYTE lpbtData, DWORD dwSize)
{
if (m_hComm == NULL)
return FALSE;
ZeroMemory(lpbtData, dwSize);
DWORD dwReaded = 0, dwToRead, dwRet;
BOOL bRet;
OVERLAPPED o = {0};
char szEvent[MAX_EVENT];
sprintf(szEvent, %d.%s.Recv, (DWORD)this, m_szComm);
// asynchronous
while (dwReaded != dwSize)
{
dwToRead = dwSize - dwReaded;
o.hEvent = CreateEvent(NULL, TRUE, FALSE, szEvent);
bRet = ReadFile(m_hComm, &lpbtData[dwReaded], dwToRead, NULL, &o);
if (!bRet)
{
dwRet = GetLastError();
if (ERROR_IO_PENDING == dwRet)
WaitForSingleObject(o.hEvent, INFINITE);
}
CloseHandle(o.hEvent);
dwReaded += o.InternalHigh;
}
return TRUE;
}
DWORD WINAPI CxComm::ListenProc(LPVOID lpParam)
{
DWORD dwID = ::GetCurrentThreadId();
char szDebug[MAX_PATH];
sprintf(szDebug, The thread 0 x%X(CxComm::ListenProc) has entered.\n, dwID);
OutputDebugString(szDebug);
CxComm pThis = (CxComm)lpParam;
if (pThis != NULL)
pThis->OnListen();
sprintf(szDebug, The thread 0 x%X(CxComm::ListenProc) has exited.\n, dwID);
OutputDebugString(szDebug);
return 0;
}
void CxComm::OnListen()
{
OVERLAPPED o = {0};
COMSTAT cs = {0};
DWORD dwEvtMask, dwError;
BOOL bRet;
char szEvent[MAX_EVENT];
sprintf(szEvent, %s.Listen, m_szComm);
while (!m_bExitThreadListen)
{
m_hEventListen = CreateEvent(NULL, TRUE, FALSE, szEvent);
o.hEvent = m_hEventListen;
bRet = WaitCommEvent(m_hComm, &dwEvtMask, &o);
if (!bRet)
{
dwError = GetLastError();
if (ERROR_IO_PENDING == dwError)
WaitForSingleObject(m_hEventListen, INFINITE);
}
if (dwEvtMask & m_dwEvent)
{
if (m_dwEvent & EV_RXCHAR) // 今朝只通知缓冲区有输入
{
if (ClearCommError(m_hComm, &dwError, &cs))
{
if (cs.cbInQue >= m_btRThreshold)
{
if (m_wndproc != NULL)
m_wndproc(m_msg.hwnd, m_msg.message,
(WPARAM)m_hComm, (LPARAM)EV_RXCHAR);
else
SendMessage(m_msg.hwnd, m_msg.message,
(WPARAM)m_hComm, (LPARAM)EV_RXCHAR);
}
}
}
}
CloseHandle(m_hEventListen);
m_hEventListen = NULL;
}
}这个类的实现项目组的代码不久不多,抛开VC6领导生成的一段,统共200多行。实现了设备串口、读写串口以及侦听串口的功能,还有以两种体式格式通知上层法度处理惩罚串口数据的功能。别的,侦听串口启动了一个新的线程,不会让界面线程梗阻的。
我写的很多实用类都很是简洁,一般都没有注释,有也是中英文混搭两句,大师习惯就好。To be continued...
读书,不要想着实用,更不要有功利心。读书只为了自身的修养。邂逅一本好书如同邂逅一位知己,邂逅一个完美之人。有时心生敬意,有时怦然心动。仿佛你心底埋藏多年的话,作者替你说了出来,你们在时光深处倾心相遇的一瞬间,情投意合,心旷神怡。