简单封装Win32串口, 采用异步读写方式
CSerialPort.h
#pragma once
#include <string>
#include <windows.h>
#include <tchar.h>
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
class CSerialPort
{
public:
CSerialPort();
CSerialPort(const CSerialPort& r) = delete;
~CSerialPort();
// 打开串口
bool Open(
int nComm, //串口号
DWORD dwBaudRate = CBR_115200, //波特率
BYTE bByteSize = 8, //数据位
BYTE bParity = NOPARITY, //校验位
BYTE bStopBits = ONESTOPBIT //停止位
);
// 打开串口
bool Open(
const _tstring& strName = _T("COM1"), //串口名
DWORD dwBaudRate = CBR_115200, //波特率
BYTE bByteSize = 8, //数据位
BYTE bParity = NOPARITY, //校验位
BYTE bStopBits = ONESTOPBIT //停止位
);
// 检测是否已经打开
bool IsOpen() const;
// 设置状态配置
bool SetState(
DWORD dwBaudRate = CBR_115200, //波特率
BYTE bByteSize = 8, //数据位
BYTE bParity = NOPARITY, //校验位
BYTE bStopBits = ONESTOPBIT //停止位
);
// 设置状态配置
bool SetState(const LPDCB lpDcb);
// 设置缓冲区大小
bool SetupComm(
DWORD dwInQueue = 14, // 输入缓冲区 1 - 14
DWORD dwOutQueue = 16 // 输出缓冲区 1 - 16
);
// 设置超时配置
bool SetTimeOut(
DWORD ReadInterval = 50, // 读间隔超时
DWORD ReadTotalMultiplier = 5, // 读时间系数
DWORD ReadTotalConstant = 1000, // 读时间常量
DWORD WriteTotalMultiplier = 10, // 写时间系数
DWORD WriteTotalConstant = 200 // 写时间常量
);
// 设置超时配置
bool SetTimeOut(const LPCOMMTIMEOUTS lpTimeOuts);
// 清空缓冲区
bool Purge(
DWORD dwFlags = PURGE_TXCLEAR | PURGE_RXCLEAR
);
// 清除错误
bool ClearError();
// 关闭串口
void Close();
// 发送数据
bool Write(
LPCVOID lpData,
DWORD cbSize,
LPDWORD lpBytesWritten = nullptr,
DWORD nTimeOut = 1000
);
// 接收数据
bool Read(
LPVOID lpData,
DWORD cbSize,
LPDWORD lpBytesRead = nullptr,
DWORD nTimeOut = 3000
);
private:
// 打开串口
bool OpenComm(int nComm);
// 打开串口
bool OpenComm(const _tstring& strName);
// 等待重叠操作完成
bool WaitForOverlapped(
HANDLE hFile,
LPOVERLAPPED lpOv,
LPDWORD lpBytesTransferred,
DWORD nTimeOut
);
private:
HANDLE m_hComm; // 串口句柄
HANDLE m_hReadEvent; // 重叠读事件句柄
HANDLE m_hWriteEvent; // 重叠写事件句柄
bool m_bExit; // 退出标记
};
CSerialPort.cpp
#include "CSerialPort.h"
#include <strsafe.h>
CSerialPort::CSerialPort()
:
m_hComm(INVALID_HANDLE_VALUE),
m_hReadEvent(nullptr),
m_hWriteEvent(nullptr),
m_bExit(false)
{
}
CSerialPort::~CSerialPort()
{
Close();
}
// 打开串口
bool CSerialPort::Open(
int nComm,
DWORD dwBaudRate/* = CBR_115200*/,
BYTE bByteSize/* = 8*/,
BYTE bParity/* = NOPARITY*/,
BYTE bStopBits/* = ONESTOPBIT*/
)
{
if (!OpenComm(nComm))
{
return false;
}
if (!SetState(dwBaudRate, bByteSize, bParity, bStopBits))
{
this->Close();
return false;
}
m_bExit = false;
return true;
}
// 打开串口
bool CSerialPort::Open(
const _tstring& strName/* = _T("COM1")*/, //串口名
DWORD dwBaudRate/* = CBR_115200*/,
BYTE bByteSize/* = 8*/,
BYTE bParity/* = NOPARITY*/,
BYTE bStopBits/* = ONESTOPBIT*/
)
{
if (!OpenComm(strName))
{
return false;
}
if (!SetState(dwBaudRate, bByteSize, bParity, bStopBits))
{
this->Close();
return false;
}
m_bExit = false;
return true;
}
bool CSerialPort::IsOpen() const
{
return INVALID_HANDLE_VALUE != m_hComm;
}
bool CSerialPort::SetState(
DWORD dwBaudRate/* = CBR_115200*/,
BYTE bByteSize/* = 8*/,
BYTE bParity/* = NOPARITY*/,
BYTE bStopBits/* = ONESTOPBIT*/
)
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
DCB dcb = { 0 };
dcb.DCBlength = sizeof(DCB);
if (!::GetCommState(m_hComm, &dcb))
{
return false;
}
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = dwBaudRate;
dcb.ByteSize = bByteSize;
dcb.Parity = bParity;
dcb.StopBits = bStopBits;
return ::SetCommState(m_hComm, &dcb);
}
bool CSerialPort::SetState(const LPDCB lpDcb)
{
if (nullptr == lpDcb)
{
return false;
}
return ::SetCommState(m_hComm, lpDcb);
}
// 设置缓冲区大小
bool CSerialPort::SetupComm(
DWORD dwInQueue/* = 14*/,
DWORD dwOutQueue/* = 16*/
)
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
return ::SetupComm(m_hComm, dwInQueue, dwOutQueue);
}
bool CSerialPort::SetTimeOut(
DWORD ReadInterval/* = 50*/,
DWORD ReadTotalMultiplier/* = 50*/,
DWORD ReadTotalConstant/* = 100*/,
DWORD WriteTotalMultiplier/* = 50*/,
DWORD WriteTotalConstant/* = 200*/
)
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
COMMTIMEOUTS comTimeOuts = { 0 };
if (!::GetCommTimeouts(m_hComm, &comTimeOuts))
{
return false;
}
comTimeOuts.ReadIntervalTimeout = ReadInterval;
comTimeOuts.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;
comTimeOuts.ReadTotalTimeoutConstant = ReadTotalConstant;
comTimeOuts.WriteTotalTimeoutMultiplier = WriteTotalMultiplier;
comTimeOuts.WriteTotalTimeoutConstant = WriteTotalConstant;
return ::SetCommTimeouts(m_hComm, &comTimeOuts);
}
bool CSerialPort::SetTimeOut(const LPCOMMTIMEOUTS lpTimeOuts)
{
if (nullptr == lpTimeOuts)
{
return false;
}
return ::SetCommTimeouts(m_hComm, lpTimeOuts);
}
bool CSerialPort::Purge(
DWORD dwFlags/* = PURGE_TXCLEAR | PURGE_RXCLEAR*/
)
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
return ::PurgeComm(m_hComm, dwFlags);
}
bool CSerialPort::ClearError()
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
DWORD dwError = 0;
COMSTAT comStat = { 0 };
return ::ClearCommError(m_hComm, &dwError, &comStat);
}
void CSerialPort::Close()
{
m_bExit = true;
if (nullptr != m_hReadEvent)
{
::SetEvent(m_hReadEvent);
::CloseHandle(m_hReadEvent);
m_hReadEvent = nullptr;
}
if (nullptr != m_hWriteEvent)
{
::SetEvent(m_hWriteEvent);
::CloseHandle(m_hWriteEvent);
m_hWriteEvent = nullptr;
}
if (INVALID_HANDLE_VALUE != m_hComm)
{
::CancelIoEx(m_hComm, nullptr);
::CloseHandle(m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
}
}
bool CSerialPort::OpenComm(int nComm)
{
TCHAR szBuf[MAX_PATH];
(void)::StringCchPrintf(szBuf, _countof(szBuf), _T(R"(\\.\COM%d)"), nComm);
// 关闭前一个已打开的串口
this->Close();
m_hComm = ::CreateFile(
szBuf,
GENERIC_READ | GENERIC_WRITE, //读写串口
0, //独占打开
nullptr,
OPEN_EXISTING, //必须存在
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //重叠操作
nullptr
);
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
m_hReadEvent = CreateEvent(nullptr, false, false, nullptr);
if (nullptr == m_hReadEvent)
{
this->Close();
return false;
}
m_hWriteEvent = CreateEvent(nullptr, false, false, nullptr);
if (nullptr == m_hWriteEvent)
{
this->Close();
return false;
}
return true;
}
bool CSerialPort::OpenComm(const _tstring& strName)
{
TCHAR szBuf[MAX_PATH];
(void)::StringCchPrintf(szBuf, _countof(szBuf), _T(R"(\\.\%s)"), strName.c_str());
// 关闭前一个已打开的串口
this->Close();
m_hComm = ::CreateFile(
szBuf,
GENERIC_READ | GENERIC_WRITE, //读写串口
0, //独占打开
nullptr,
OPEN_EXISTING, //必须存在
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //重叠操作
nullptr
);
if (INVALID_HANDLE_VALUE == m_hComm)
{
return false;
}
m_hReadEvent = CreateEvent(nullptr, false, false, nullptr);
if (nullptr == m_hReadEvent)
{
this->Close();
return false;
}
m_hWriteEvent = CreateEvent(nullptr, false, false, nullptr);
if (nullptr == m_hWriteEvent)
{
this->Close();
return false;
}
return true;
}
bool CSerialPort::WaitForOverlapped(
HANDLE hFile,
LPOVERLAPPED lpOv,
LPDWORD lpBytesTransferred,
DWORD nTimeOut
)
{
bool bResult = false;
DWORD dwBytesTransferred = 0;
DWORD dwWait = ::WaitForSingleObject(lpOv->hEvent, nTimeOut);
switch (dwWait)
{
case WAIT_OBJECT_0:
{
if (::GetOverlappedResult(hFile,
lpOv,
&dwBytesTransferred,
true
))
{
bResult = true;
}
else
{
bResult = false;
}
if (lpBytesTransferred)
{
*lpBytesTransferred = dwBytesTransferred;
}
}
break;
case WAIT_TIMEOUT:
break;
default:
break;
}
if (!bResult && !m_bExit)
{
::CancelIoEx(hFile, lpOv);
}
return bResult;
}
bool CSerialPort::Write(
LPCVOID lpData,
DWORD cbSize,
LPDWORD lpBytesWritten,
DWORD nTimeOut/* = 1000*/
)
{
if (INVALID_HANDLE_VALUE == m_hComm ||
nullptr == lpData ||
0 == cbSize ||
m_bExit)
{
return false;
}
OVERLAPPED ov = { 0 };
BOOL bResult = FALSE;
ov.hEvent = m_hWriteEvent;
do
{
bResult = ::WriteFile(
m_hComm,
lpData,
cbSize,
lpBytesWritten,
&ov
);
if (bResult)
{
break;
}
if (ERROR_IO_PENDING != ::GetLastError())
{
break;
}
bResult = WaitForOverlapped(m_hComm, &ov, lpBytesWritten, nTimeOut);
} while (false);
return bResult;
}
bool CSerialPort::Read(
LPVOID lpData,
DWORD cbSize,
LPDWORD lpBytesRead/* = nullptr*/,
DWORD nTimeOut/* = 3000*/
)
{
if (INVALID_HANDLE_VALUE == m_hComm ||
nullptr == lpData ||
0 == cbSize ||
m_bExit)
{
return false;
}
OVERLAPPED ov = { 0 };
BOOL bResult = FALSE;
ov.hEvent = m_hReadEvent;
do
{
bResult = ::ReadFile(
m_hComm,
lpData,
cbSize,
lpBytesRead,
&ov
);
if (bResult)
{
break;
}
if (ERROR_IO_PENDING != ::GetLastError())
{
break;
}
bResult = WaitForOverlapped(m_hComm, &ov, lpBytesRead, nTimeOut);
} while (false);
return bResult;
}