📄 异步的com基类 serialport.cpp
字号:
// SerialPort.cpp: implementation of the CSerialPort class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "sms.h"
#include "SerialPort.h"
#include <Winreg.h> //RegEnumValue所在头文件
#include <atlbase.h> //CRegKey所在头文件
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSerialPort::CSerialPort()
{
}
CSerialPort::~CSerialPort()
{
}
//////////////////////////////////////////////////////////////////////////
//取得系统上的所有COM口
void CSerialPort::GetCOMList(vector<CString> & PortList)
{
CRegKey RegKey;
int nCount = 0;
//CString * pNameList=new CString[5];
//CString * pPortList=new CString[5];
if(RegKey.Open(HKEY_LOCAL_MACHINE, "Hardware\\DeviceMap\\SerialComm") == ERROR_SUCCESS)
{
while(true)
{
char ValueName[_MAX_PATH];
unsigned char ValueData[_MAX_PATH];
DWORD nValueSize = _MAX_PATH;
DWORD nDataSize = _MAX_PATH;
DWORD nType;
//RegEnumValue函数的作用是,从被指定打开的注册表项中列举所有的值
//这函数每调用一次就会拷贝一个索引值的名称和数据块
if(::RegEnumValue(HKEY(RegKey), nCount, ValueName, &nValueSize, NULL, &nType, ValueData, &nDataSize) == ERROR_NO_MORE_ITEMS)
{
break;
}
// if(pNameList)
// pNameList[nCount] = ValueName;
//
// if(pPortList)
// pPortList[nCount] = ValueData;
CString temp=ValueData;
PortList.push_back(temp);
TRACE(PortList[nCount]+"\n");
nCount++;
}
}
}
BOOL CSerialPort::OpenCOMPort(CString sPort,int nBaudRate,int nByteSize,int nParity,int nStopBit)
{
BOOL result=FALSE;
//打开文件
hCom=CreateFile((LPCSTR)(LPCTSTR(sPort)), //指定要找开或创建的文件 这里指定串口名或设置的路径
GENERIC_READ|GENERIC_WRITE, //提供读写方式
0, //这里设置共享方式,这里采用独占方式
NULL, //设置安全属性,我们采用默认的设置
OPEN_EXISTING, //指定打开一个已存在的文件,由于COM口在系统中被认为是一个以存在的文件 打开而不是创建
FILE_ATTRIBUTE_NORMAL |FILE_FLAG_OVERLAPPED ,//FILE_ATTRIBUTE_NORMAL 属性 指明该文件不使用文件属性 FILE_FLAG_OVERLAPPED 标记指定使用重叠方式
NULL ); //不需要参照模板文件
if(INVALID_HANDLE_VALUE==hCom) //如果打开COM口文件失改,将会返回INVALID_HANDLE_VALUE这个错误
{
TRACE("打开COM失败!");
return FALSE;
}
//////////////////////////////////////////////////////////////////////////
//清空异步读写参数
memset(&m_OverlappedRead,0,sizeof(OVERLAPPED));
memset(&m_OverlappedWrite,0,sizeof(OVERLAPPED));
//////////////////////////////////////////////////////////////////////////
//读取通信状态,并对特定属性进行修改
DCB dcb;
//通过GetCommState函数可获取指定通信端口的状态
GetCommState(hCom,&dcb); //取得打开COM口的状态
dcb.BaudRate=nBaudRate; //波特率
dcb.ByteSize=nByteSize; //字节大小
dcb.Parity=nParity; //奇偶校验
dcb.StopBits=nStopBit; //停止位
//通过dcb结构体来重置一下通信状态
//SetCommState(hCom,&dcb);
//////////////////////////////////////////////////////////////////////////
//设置通信超时
//设置通信超时信息结构体
COMMTIMEOUTS commTimeouts;
commTimeouts.ReadIntervalTimeout=100; // 读字符间隔超时时间: 100 ms
commTimeouts.ReadTotalTimeoutConstant=500 ; // 基本的(额外的)读超时时间: 500 ms
commTimeouts.ReadTotalTimeoutMultiplier=1; // 读操作时每字符的时间: 1 ms (n个字符总共为n ms)
commTimeouts.WriteTotalTimeoutConstant=100; // 基本的(额外的)写超时时间: 100 ms
commTimeouts.WriteTotalTimeoutMultiplier=1;// 写操作时每字符的时间: 1 ms (n个字符总共为n ms)
//将超时信信息结构体的内容设置到指定的通信设备上去
SetCommTimeouts(hCom,&commTimeouts);
//获取信号句柄
m_OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //创建事件,并将事件处理非激发状态,并且通过第二个参数使该事件使用手动重置
m_OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//////////////////////////////////////////////////////////////////////////
//初始化通信设备的缓冲区
//SetupComm函数来初始化一个指定通信设备的通信参数 通过句柄来指明要修改参数的设备
//SetupComm(hCom/*设备的句柄*/ ,4096 /*输入缓冲区大小*/,1024 /*输出缓冲区大小*/);
if( !SetCommState(hCom,&dcb) || //通过dcb结构体来重置一下通信状态
!SetupComm(hCom/*设备的句柄*/ ,4096 /*输入缓冲区大小*/,1024 /*输出缓冲区大小*/) ||
!SetCommMask(hCom, EV_RXCHAR) || //为一个通信设备指定一组被监听的事件
NULL == m_OverlappedRead.hEvent || //判断读事件是否创建成功
NULL == m_OverlappedWrite.hEvent ) //判断写事件是否创建成功
{
DWORD dwError = GetLastError(); //获取最后的错误信息
if( m_OverlappedRead.hEvent != NULL ) CloseHandle( m_OverlappedRead.hEvent );
if( m_OverlappedWrite.hEvent != NULL ) CloseHandle( m_OverlappedWrite.hEvent );
CloseHandle(hCom);
hCom=NULL;
return FALSE;
}
//删除指定通信资源的输入和输出缓冲区中的所有字符
PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
TRACE("连接串口成功");
return TRUE;
}
BOOL CSerialPort::CloseSerialPort()
{
BOOL result=FALSE;
if(NULL==hCom)
{
return TRUE;
}
//为一个通信设备指定一组被监听的事件
SetCommMask(hCom,NULL);
//SetEvent(m_OverlappedRead.hEvent);//将事件置为激发状态
//SetEvent(m_OverlappedWrite.hEvent);//将事件置为激发状态
if( NULL != m_OverlappedRead.hEvent) CloseHandle( m_OverlappedRead.hEvent);
if( NULL != m_OverlappedWrite.hEvent) CloseHandle( m_OverlappedWrite.hEvent);
result=CloseHandle(hCom);
if(TRUE == result)
{
hCom=NULL;
}
return result;
}
BOOL CSerialPort::IsOpen()
{
return hCom != INVALID_HANDLE_VALUE;
}
BOOL CSerialPort::Read(LPVOID buffer, DWORD bufferLength)
{
if(!IsOpen())
{
return FALSE;
}
DWORD dwError;
COMSTAT comStat;
DWORD readLength=0;
//这里的的作用是指,还没有执行ReadFile操作时,串口能接收的字节数
ClearCommError( hCom, &dwError, &comStat ) ;//取得串设备的当前状和错误,并清除错误
readLength = min( (DWORD) bufferLength, comStat.cbInQue ) ;
if(ClearCommError(hCom,&dwError,&comStat) && dwError>0)
{
//清除通信源的输入输出缓冲区中的所有字符
PurgeComm(hCom,PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR);
return FALSE;
}
if(readLength>0)
{
//对于异步方式要指定重叠结构,可不指定已读取节数 将读取操作跟m_OverlappedRead结构体中的hEvent事件关连起来
if(!::ReadFile(hCom,buffer,bufferLength,&readLength,&m_OverlappedRead))
{
//如果返回的错误是IO挂起错误
if(GetLastError()==ERROR_IO_PENDING)
{
//等侍读取事件变成激发状态后才返回 只有当文件读取完成后,与之关连的事件会从非激发状态变成激发状态
if(!WaitForSingleObject(m_OverlappedRead.hEvent,INFINITE))
{
//将事件置成非激发状态
ResetEvent(m_OverlappedRead.hEvent);
if(GetLastError()!=WAIT_OBJECT_0)
{
return FALSE;
}
}
// //检索指定文件的重叠操作的结果 成功返回非0值
// while(!GetOverlappedResult(hCom,&m_OverlappedRead,&readLength,TRUE))
// {
// //判断IO操作是否完成 即文件是否读取完成
// if(GetLastError()!=ERROR_IO_INCOMPLETE)
// {
// //用来接收一个通信错误信息和一个通信设备的当前状态报表
// ClearCommError(hCom,&dwError,&comStat);
// break;
// }
// }
}
else
{
//当该函数被调用后,将清除设备的错误标记,以允许附加输入输出操作
//同时返回一个通信错误信息和一个通信设备的当前状态报表
ClearCommError(hCom,&dwError,&comStat);
return FALSE;
}
}
}
return TRUE;
}
BOOL CSerialPort::Write(LPVOID buffer, DWORD bufferLength)
{
//判断是否连接
if(!IsOpen())
return FALSE;
DWORD readLength;
DWORD dwError;
COMSTAT comStat;
//对于异步方式要指定重叠结构,可不指定已写入字节数 将写入操作跟m_OverlappedWrite结构体中的hEvent事件关连起来
if(!WriteFile(hCom,buffer,bufferLength,&readLength,&m_OverlappedWrite))
{
//判断最近一次错误信息是不是I/O挂起错误
if(GetLastError()==ERROR_IO_PENDING)
{
//等侍读取事件变成激发状态后才返回 只有当文件读取完成后,与之关连的事件会从非激发状态变成激发状态
if(!WaitForSingleObject(m_OverlappedWrite.hEvent,INFINITE))
{
if(GetLastError()!=WAIT_OBJECT_0)
{
return FALSE;
}
//将事件置成非激发状态
ResetEvent(m_OverlappedWrite.hEvent);
}
// while(!GetOverlappedResult(hCom,&m_OverlappedWrite,&readLength,TRUE))
// {
// //判断IO操作是否完成 即文件是否读取完成
// if(GetLastError()!=ERROR_IO_INCOMPLETE) //ERROR_IO_INCOMPLETE 为未完成IO错误
// {
// ClearCommError(hCom,&dwError,&comStat);
// break;
// }
// }
}
else
{
ClearCommError(hCom,&dwError,&comStat);
return FALSE;
}
}
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -