📄 serialcomm.cpp
字号:
// SerialComm.cpp: implementation of the CSerialComm class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SerialComm.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSerialComm::CSerialComm()
{
#if !defined(SC_MSCOMOBJECT)
m_bAsyncOutput = FALSE; //同步写方式
m_hCOM = NULL; //串行通讯端口句柄
::ZeroMemory(&m_stDCB, sizeof(DCB));
::ZeroMemory(&m_stCTO, sizeof(COMMTIMEOUTS));
::ZeroMemory(m_atcCurCOM, sizeof(TCHAR) * SC_MAXCOMNAME);
::ZeroMemory(&m_stROverlapped, sizeof(m_stROverlapped));
::ZeroMemory(&m_stWOverlapped, sizeof(m_stWOverlapped));
m_stWOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_stROverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_bShowCommConfigDialog = FALSE;
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
CSerialComm::~CSerialComm()
{
Close();
#if !defined(SC_MSCOMOBJECT)
if (NULL != m_stROverlapped.hEvent)
{
::CloseHandle(m_stROverlapped.hEvent);
}
if (NULL != m_stWOverlapped.hEvent)
{
::CloseHandle(m_stWOverlapped.hEvent);
}
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//获取当前系统中安装的串行通讯端口
//<参数>
// pppCOMs: 用于接收一个指向串行通讯端口名集的指针
//<返回值>
// 返回当前系统中安装的串行通讯端口名集。
int CSerialComm::GetCOMs(LPCTSTR ** pppCOMs)
{
#if !defined(SC_MSCOMOBJECT)
if (NULL == pppCOMs)
return int(0);
static TCHAR atcCOMs[SC_MAXCOMNUM][SC_MAXCOMNAME];
static LPTSTR apCOMs[SC_MAXCOMNUM];
::ZeroMemory(atcCOMs, sizeof(TCHAR) * SC_MAXCOMNUM * SC_MAXCOMNAME);
int iCount = 0;
HANDLE hCOM = NULL;
for (int i = 0; i < SC_MAXCOMNUM; i++)
{
apCOMs[i] = atcCOMs[i];
::_stprintf(atcCOMs[iCount], _T("COM%d"), i+1);
//打开串口
hCOM = ::CreateFile(atcCOMs[iCount],
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
//如果串口打开失败...
if (INVALID_HANDLE_VALUE == hCOM || NULL == hCOM)
{
DWORD dwLastError = ::GetLastError();
if (ERROR_FILE_NOT_FOUND != dwLastError)
++iCount;
else
::ZeroMemory(atcCOMs[iCount], sizeof(TCHAR) * SC_MAXCOMNAME);
}
else
{
::CloseHandle(hCOM);
++iCount;
}
}
if (0 < iCount)
*pppCOMs = (LPCTSTR *)apCOMs;
return int(iCount);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//打开指定串行通讯端口并指定其波特率和校验方式
//<参数>
// lpszCOMName: 指定端口名称
// dwBaudrate: 指定波特率
// dwParity: 指定校验方式
//<返回值>
// 如果成功返回TRUE,否则返回FALSE。
BOOL CSerialComm::Open(LPCTSTR lpszCOMName, DWORD dwBaudrate/* = 0xFFFFFFFFl*/, DWORD dwParity/* = 0xFFFFFFFFl*/)
{
#if !defined(SC_MSCOMOBJECT)
Close();
if (NULL == lpszCOMName || FALSE != m_bShowCommConfigDialog)
return BOOL(FALSE);
//打开串口
m_hCOM = ::CreateFile(lpszCOMName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
//如果串口打开失败...
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
{
m_hCOM = NULL;
return BOOL(FALSE);
}
//设置缺省配置
SetDefaultConfig(dwBaudrate, BYTE(dwParity));
::lstrcpyn(m_atcCurCOM, lpszCOMName, SC_MAXCOMNAME - 1);
return BOOL(TRUE);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//关闭当前使用的串行端口
//<参数> 无
//<返回值> 无
void CSerialComm::Close()
{
#if !defined(SC_MSCOMOBJECT)
if (NULL != m_hCOM)
{
::PurgeComm(m_hCOM, PURGE_RXABORT | PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
::CloseHandle(m_hCOM);
m_hCOM=NULL;
::ZeroMemory(m_atcCurCOM, sizeof(TCHAR) * SC_MAXCOMNAME);
}
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//判断当前串行通讯端口是否已经打开
//<参数> 无
//<返回值>
// 如果已经打开则返回已打开串口的名字,否则返回NULL。
LPCTSTR CSerialComm::IsOpened()
{
#if !defined(SC_MSCOMOBJECT)
return LPCTSTR((NULL != m_hCOM && INVALID_HANDLE_VALUE != m_hCOM) ? m_atcCurCOM : NULL);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
#if !defined(SC_MSCOMOBJECT)
//设置当前串行通讯端口的缺省配置
//<参数>
// dwBaudrate: 指定波特率,如果为0xFFFFFFFF表示保持原有设置值
// ucParity: 指定校验方式,如果为0xFFFF表示保持原有设置值
//<返回值> 无
void CSerialComm::SetDefaultConfig(DWORD dwBaudrate, BYTE ucParity)
{
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
return;
if (FALSE != ::GetCommState(m_hCOM, &m_stDCB))
{
if (0xFFFFFFFF != dwBaudrate)
m_stDCB.BaudRate = FormatBaudrate(dwBaudrate);
if (0xFF != ucParity)
{
m_stDCB.Parity = FormatParity(ucParity);
m_stDCB.fParity = BOOL(m_stDCB.Parity != NOPARITY);
}
m_stDCB.DCBlength = sizeof(DCB); //DCB数据结构长度
m_stDCB.ByteSize = 8; //数据位
m_stDCB.StopBits = ONESTOPBIT; //停止位
m_stDCB.fAbortOnError = FALSE; //发生通讯错误时不终止读写
m_stDCB.fBinary = TRUE; //以二近制方式通讯
::SetCommState(m_hCOM, &m_stDCB);
}
if (FALSE != ::GetCommTimeouts(m_hCOM, &m_stCTO))
{
int iBits = 1 + m_stDCB.ByteSize + ((m_stDCB.StopBits == ONESTOPBIT) ? 1 : 2);
m_stCTO.ReadIntervalTimeout = 10; //10ms的区间超时
m_stCTO.ReadTotalTimeoutMultiplier = (iBits * 1000) / m_stDCB.BaudRate; //传输一个字节所需的时间
if (0 == m_stCTO.ReadTotalTimeoutMultiplier)
m_stCTO.ReadTotalTimeoutMultiplier = 1;
m_stCTO.ReadTotalTimeoutConstant = m_stCTO.ReadTotalTimeoutMultiplier * 10;
m_stCTO.WriteTotalTimeoutMultiplier = ((iBits + 2) * 1000) / m_stDCB.BaudRate; //传输一个字节所需的时间
if (0 == m_stCTO.WriteTotalTimeoutMultiplier)
m_stCTO.WriteTotalTimeoutMultiplier = 1;
m_stCTO.WriteTotalTimeoutConstant = m_stCTO.WriteTotalTimeoutMultiplier * 10;
::SetCommTimeouts(m_hCOM, &m_stCTO);
}
}
#endif //if !defined(SC_MSCOMOBJECT)
//从当前串行通讯端口读取数据
//<参数>
// pBuff: 指定用于接收数据的缓冲区
// lReadSize: 指定期望读取的数据长度
//<返回值>
// 返回正确读取的字节数。如果指定一个NULL缓冲区或期望读取的数据长度不大于0,
//则返回当前系统内部读缓冲区中的字节数。出错时返回值小于0。
long CSerialComm::Read(LPVOID pBuff, long lReadSize)
{
#if !defined(SC_MSCOMOBJECT)
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
return long(-1);
//如果入口参数无效则试图返回输入缓冲区中的数据长度
if (NULL == pBuff || 0 > lReadSize)
{
COMSTAT stCOMStat;
DWORD dwErr;
if (FALSE == ::ClearCommError(m_hCOM, &dwErr, &stCOMStat))
return long(-2);
return long(stCOMStat.cbInQue);
}
DWORD dwReadedSize = 0;
long lReaded = 0;
int iCount = (lReadSize + 3) >> 2;
while (iCount > 0)
{
--iCount;
::ResetEvent(m_stROverlapped.hEvent);
//如果读取失败...
if (FALSE == ::ReadFile(m_hCOM, LPBYTE(pBuff)+lReaded, lReadSize, &dwReadedSize, &m_stROverlapped))
{
//如果是重叠IO...
if (ERROR_IO_PENDING == GetLastError())
{
if (FALSE != ::GetOverlappedResult(m_hCOM, &m_stROverlapped, &dwReadedSize, TRUE))
{
lReaded += long(dwReadedSize);
lReadSize -= long(dwReadedSize);
if (0 >= lReadSize || 0 == dwReadedSize)
return long(lReaded);
else
{
iCount = min((lReadSize + 3) >> 2, iCount);
}
}
} //if (ERROR_IO_PENDING == GetLastError())
} //if (FALSE == ::ReadFile)
else
{
lReaded += long(dwReadedSize);
//读取完成
return long(lReaded);
}
} //while(dwSize)
return long(lReaded);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//向当前串行通讯端口写数据
//<参数>
// pData: 指定待写数据
// lWriteSize: 指定待写数据长度
//<返回值>
// 返回正确写出的字节数。如果指定pData为NULL或待写数据长度不大于0,
//则返回当前系统内部写缓冲区中的字节数。出错时返回值小于0。
long CSerialComm::Write(LPVOID pData, long lWriteSize)
{
#if !defined(SC_MSCOMOBJECT)
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
return long(-1);
COMSTAT stCOMStat;
DWORD dwErr = 0;
//如果入口参数无效则试图返回输入缓冲区中的数据长度
if (NULL == pData || 0 > lWriteSize)
{
if (FALSE == ::ClearCommError(m_hCOM, &dwErr, &stCOMStat))
return long(-2);
return long(stCOMStat.cbOutQue);
}
DWORD dwWritedSize = 0;
long lWritedTatol = 0;
if (FALSE != m_bAsyncOutput)
{
::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE);
dwWritedSize = 0;
}
while (0 < lWriteSize)
{
::ResetEvent(m_stWOverlapped.hEvent);
//如果发送失败...
if (FALSE == ::WriteFile(m_hCOM, LPBYTE(pData) + lWritedTatol, DWORD(lWriteSize), &dwWritedSize, &m_stWOverlapped))
{
//如果是重叠IO...
if (ERROR_IO_PENDING == ::GetLastError())
{
//如果重叠IO失败...
if (FALSE == ::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE))
{
if (FALSE != m_bAsyncOutput)
return long(lWritedTatol);
DWORD dwWait = m_stCTO.WriteTotalTimeoutMultiplier * lWriteSize + m_stCTO.WriteTotalTimeoutConstant;
if (WAIT_TIMEOUT == ::WaitForSingleObject(m_stWOverlapped.hEvent, dwWait))
return long(-3);
if (FALSE == ::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE))
return long(-4);
}
if (0 == dwWritedSize)
return long(lWritedTatol);
lWritedTatol += long(dwWritedSize);
lWriteSize -= long(dwWritedSize);
} //if (ERROR_IO_PENDING == ::GetLastError())
else
return long(-5);
}
else
{
lWritedTatol += long(dwWritedSize);
lWriteSize -= long(dwWritedSize);
}
}
if (FALSE != m_bAsyncOutput)
return long(lWritedTatol);
//清空软件缓冲区
::FlushFileBuffers(m_hCOM);
DCB stDCB;
if (FALSE != ::GetCommState(m_hCOM, &stDCB))
{
int iBits = 1 + stDCB.ByteSize + ((stDCB.StopBits == ONESTOPBIT) ? 1 : 2); //一个字节的实际传输位数
int iBytesTime = (iBits * 1000 * min(16 + 2, dwWritedSize + 2)) / stDCB.BaudRate; //传输若干字节所需要的时间
::Sleep(iBytesTime); //等待硬件缓冲区空
}
return long(lWritedTatol);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//对Win32API CommConfigDialog函数的封装
//<参数> 无
//<返回值>
// 如果成功返回TRUE,否则返回FALSE。
BOOL CSerialComm::CommConfigDialog()
{
#if !defined(SC_MSCOMOBJECT)
if (NULL != IsOpened())
{
DWORD dwCCSize;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -