//CSerialPort.h
#ifndef CSERIALPORT_H_
#define CSERIALPORT_H_#pragma once#include <windows.h>
#include <vector>
#include <string>using namespace std;typedef vector<string> Comms;
class CSerialPort
{
//成员方法
public:
CSerialPort(int _aComID = 1); // 带序号的构造函数
virtual ~CSerialPort(void); bool OpenPort( const char* _aName, bool _aStartWatchThread = true);
bool OpenPort( int _aID ); static void GetCommPorts( Comms& _ac ); //串口被被的程序打开,则返回false
bool IsPortOpen( void ); // 关闭串口
void ClosePort( void ); // 不带参数OpenPort
inline bool OpenPort( void )
{
return OpenPort( iComID );
} // 设置串口参数manual==true时,手动设置
bool SetCommPortParams( DCB& _aDCB, bool manual = false);
bool SetCommPortParams( DWORD _aBaudRate = CBR_9600,
BYTE _aByteSize = 8,
BYTE _aParity = NOPARITY,
BYTE _aStopBits = ONESTOPBIT
);
// 获取串口参数,保存在DCB结构体中
bool GetCommParams(DCB& _aDCB);
// 获得波特率
bool GetBaudRate(DWORD& _aBaudRate);
// 获得数据位
bool GetDataBit(BYTE& _aByteSize);
// 获得校验位
bool GetParity(BYTE& _aParity);
// 获得停止位
bool GetStopBits(BYTE& _aStopBits);
// 设置延时
bool SetTimeouts(
DWORD _aReadIntervalTimeout = 0,
DWORD _aReadTotalTimeoutMultiplier = 10,
DWORD _aReadTotalTimeoutConstant = 1000,
DWORD _aWriteTotalTimeoutMultiplier = 10,
DWORD _aWriteTotalTimeoutConstant = 1000
);
// 利用COMMTIMEOUTS结构体,获取延时设置
bool GetTimeouts(COMMTIMEOUTS& _aCO);
// 设置接缓冲区大小
inline bool SetInputBuffer(unsigned long _aInLength);
// 设置发送缓冲区大小
bool SetOutoutBuffer(unsigned long _aOutLength);
// 清除发送缓冲区
bool ClearOutputBuffer(void);
// 清除接收缓冲区
bool ClearInputBuffer(void);
// 写数据 unsigned long SendData(LPVOID _alpBuff, DWORD _aWritingLength);
// 发送任意数据
unsigned long SendData(char* _awBuff, unsigned long _aWrittenNum, char* _awFormat, ...);
// 接收数据
unsigned long RecvData( void* rcvBuf,
long _aNumberOfBytesToRead,
unsigned long& _aNumberOfBytesRead );protected:
inline void Init(void);private:
void InitDCB(); //初始化DCB串口参数结构体
inline bool InintTimeOuts(); //初始化延时结构体
// 设置延时
inline bool SetTimeouts(COMMTIMEOUTS& _aCO);
inline bool InitIOBuffer(DWORD _aInBufferSize, DWORD _aOutBufferSize);
// 该函数如果检测,当前窗口未打开,或被别的程序占用,则抛出异常!
inline void JudgePortInfo(void);
void InitOverLapped(void);
//启动监视线程
static DWORD WINAPI WatchIOEvent(LPVOID _alpParameter);
//成员变量
private:
int iComID;
// 表示要操作串口句柄
HANDLE ihCommHandle;
//设置串口参数,如波特率,停止位,数据位,校验位等等
DCB iDCB;
// 设置读写超时
COMMTIMEOUTS iCTO;
// 记录当前设置的串口接收缓冲区大小
DWORD iInputBuffSize;
// 记录当前设置的串口发送缓冲区大小
DWORD iOutputBuffSize;
OVERLAPPED iWriteOverLapped;
OVERLAPPED iReadOverLapped;
HWND ihWnd; //接收消息的窗口句柄
HANDLE ihThread;
//串口状态
COMSTAT iState;
//Mask Event
DWORD iEvtMask;
};
#endif
//cpp文件见 2楼
//CSerialPort.cpp
#include "StdAfx.h"
#include ".\serialport.h"
#include <cassert>
#define MAX 1000CSerialPort::CSerialPort ( int _aComID )
: ihCommHandle ( INVALID_HANDLE_VALUE ),
iComID ( _aComID ) ,
ihWnd ( NULL ),
ihThread ( INVALID_HANDLE_VALUE ),
iEvtMask ( 0 )
{
assert(_aComID >0 && _aComID < 1025 );
OpenPort( _aComID );
}
CSerialPort::~CSerialPort( void )
{
CloseHandle( ihCommHandle );
ihCommHandle = INVALID_HANDLE_VALUE; CloseHandle( ihThread );
ihThread = INVALID_HANDLE_VALUE;}void CSerialPort::GetCommPorts( Comms& _ac )
{
char _coms[10] = "\\\\.\\COM";
for(int i = 1; i < MAX; i++)
{
char ID[ 2 ] = "1";
_ltoa( i, ID, 10 );
strcat( _coms, ID );
HANDLE _hCommHandle = CreateFile(
_coms,
GENERIC_READ | GENERIC_WRITE, //读写兼之
0,
NULL, //
OPEN_EXISTING, //操作串口,必须指定为该值
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL //操作串口,必须指定为该值
); //该函数打开串口成功会返回一非零的句柄
//cout << (INVALID_HANDLE_VALUE == _hCommHandle) << endl; if( ERROR_FILE_NOT_FOUND == GetLastError() ) //如果创建句柄成功,则返回0
{ //ERROR_FILE_NOT_FOUND is a long integer value 2
strcpy(_coms,"\\\\.\\COM");
continue;
}
if( _hCommHandle ) //如果句柄非零,就将窗口名字压入vector
{
string _tmp( _coms );
_ac.push_back( _tmp );
CloseHandle( _hCommHandle );
strcpy( _coms,"\\\\.\\COM" );
} //End if
}//End for
}//End member function GetCommPorts
bool CSerialPort::OpenPort ( const char* _aName, bool _aStartWatchThread )
{
if( IsPortOpen() )
return true;
string stdName( _aName );
if( stdName.length() <=5 )
stdName = stdName.append("\\\\.\\").append( _aName ); /*{ //此处注释掉的只是去掉了检查指定的串口是不是存在,勿删
Comms _ac;
GetCommPorts( _ac );
for( int i = 0; i < _ac.size(); i ++ )
{
if( _ac[ i ].compare( string( _aName ) ) == 0 )
break;
}// End for if( i == _ac.size() )
{
string msg = _aName;
msg.append( " are not existent in your computer!" );
//msg.append("在您的计算机上不存在!");
throw exception( msg.c_str() );
} //End if
}*/ //If i < _ac.size() ,open it
ihCommHandle = CreateFile(
stdName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, //FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL
);
Init ();
DWORD _tID;
if ( _aStartWatchThread )
{
ihThread = CreateThread(NULL, 0, WatchIOEvent,this, 0, &_tID); //打开监视线程
if ( ! ihThread )
{
throw exception ( "打开串口时,监视线程启动错误" );
}
}
return true;
} //End OpenPort (const char *)bool CSerialPort::OpenPort( int _aID )
{
assert(_aID >0 && _aID < 100 );
char whichSPort[10] = "\\\\.\\COM";
char _tID[3] = {"1"};
_ltoa( _aID, _tID, 10);
strcat( whichSPort, _tID );
return OpenPort( whichSPort );
}//串口被其他的程序打开,则返回false
bool CSerialPort::IsPortOpen( void )
{
return ihCommHandle != INVALID_HANDLE_VALUE;
}
// 关闭串口
void CSerialPort::ClosePort(void)
{
assert( ihCommHandle != INVALID_HANDLE_VALUE );
CloseHandle( ihCommHandle );
ihCommHandle = INVALID_HANDLE_VALUE; //关闭之后记得要把串口句柄设为无效
CloseHandle( ihThread );
ihThread = INVALID_HANDLE_VALUE; //关闭之后记得要把串口句柄设为无效;
}// 设置串口参数manual==true时,手动设置
bool CSerialPort::SetCommPortParams(DCB& _aDCB, bool manual)
{
JudgePortInfo();
if( manual )
{
return SetCommState( ihCommHandle, &_aDCB ) ? true : false;
}
return SetCommState( ihCommHandle, &iDCB ) ? true : false;}bool CSerialPort::SetCommPortParams( DWORD _aBaudRate,
BYTE _aByteSize,
BYTE _aParity,
BYTE _aStopBits
)
{
JudgePortInfo();
DCB _tDCB;
memset( &_tDCB, 0, sizeof( _tDCB ) );
if( GetCommState( ihCommHandle, &_tDCB ) == TRUE )
{
_tDCB.DCBlength = sizeof( _tDCB );
_tDCB.BaudRate = _aBaudRate;
_tDCB.ByteSize = _aByteSize;
_tDCB.Parity = _aParity;
_tDCB.StopBits = _aStopBits;
bool success = SetCommState( ihCommHandle, &_tDCB ) == TRUE;
return success;
}
return false;
}void CSerialPort::InitDCB( )
{
memset( &iDCB, 0, sizeof( iDCB ) );
if( GetCommState( ihCommHandle, &iDCB ) == TRUE )
{
iDCB.DCBlength = sizeof( DCB );
//以下是默认参数
iDCB.BaudRate = CBR_38400;
iDCB.StopBits = ONESTOPBIT;
iDCB.ByteSize = 5;
iDCB.Parity = NOPARITY;
iDCB.fNull = FALSE;
}
}
// 获取串口参数,保存在DCB结构体中
bool CSerialPort::GetCommParams(DCB& _aDCB)
{
JudgePortInfo();
return GetCommState( ihCommHandle , &_aDCB) == TRUE;
}// 获得波特率
bool CSerialPort::GetBaudRate(DWORD& _aBaudRate)
{
DCB _tDCB;
memset( &_tDCB, 0, sizeof( _tDCB) );
JudgePortInfo();
bool success = GetCommState( ihCommHandle, &_tDCB ) == TRUE;
if( success )
{
_aBaudRate = _tDCB.BaudRate;
return true;
}
return false;
}// 获得数据位
bool CSerialPort::GetDataBit(BYTE& _aByteSize)
{
DCB _tDCB;
memset( &_tDCB, 0, sizeof( _tDCB) );
JudgePortInfo();
bool success = GetCommState( ihCommHandle, &_tDCB ) == TRUE;
if( success )
{
_aByteSize = _tDCB.ByteSize;
return true;
}
return false;
}// 获得校验位
bool CSerialPort::GetParity(BYTE& _aParity)
{
DCB _tDCB;
memset( &_tDCB, 0, sizeof( _tDCB) );
JudgePortInfo();
bool success = GetCommState( ihCommHandle, &_tDCB ) == TRUE;
if( success )
{
_aParity = _tDCB.Parity;
return true;
}
return false;
}// 获得停止位
bool CSerialPort::GetStopBits(BYTE& _aStopBits)
{
DCB _tDCB;
memset( &_tDCB, 0, sizeof( _tDCB) );
JudgePortInfo();
bool success = GetCommState( ihCommHandle, &_tDCB ) == TRUE;
if( success )
{
_aStopBits = _tDCB.StopBits;
return true;
}
return false;
}void CSerialPort::Init(void)
{
if( ihCommHandle == INVALID_HANDLE_VALUE )
throw exception( "指定设备被其他程序占用" );
SetCommPortParams();
InitDCB( ); //初始化DCB串口参数结构体
InitIOBuffer(1024, 512 ); //设置默认串口缓冲区
InintTimeOuts(); //设置延时结构
}bool CSerialPort::InintTimeOuts()
{
if ( GetCommTimeouts( ihCommHandle, &iCTO ) )
{
iCTO.ReadIntervalTimeout = 0;
iCTO.ReadTotalTimeoutMultiplier = 1;
iCTO.ReadTotalTimeoutConstant = 1000;
iCTO.WriteTotalTimeoutMultiplier = 1;
iCTO.WriteTotalTimeoutConstant = 1000;
cout << "timeout error = " << GetLastError() << endl;
return true;
}
return false;
}// 设置延时
bool CSerialPort::SetTimeouts(COMMTIMEOUTS& _aCO )
{
if(! GetCommTimeouts( ihCommHandle, & _aCO ) )
return false;
return SetCommTimeouts( ihCommHandle, &_aCO ) == TRUE;
}// 设置延时
bool CSerialPort::SetTimeouts(
DWORD _aReadIntervalTimeout,
DWORD _aReadTotalTimeoutMultiplier,
DWORD _aReadTotalTimeoutConstant,
DWORD _aWriteTotalTimeoutMultiplier,
DWORD _aWriteTotalTimeoutConstant
)
{
JudgePortInfo();
memset( &iCTO, 0, sizeof(iCTO) );
iCTO.ReadIntervalTimeout = _aReadIntervalTimeout;
iCTO.ReadTotalTimeoutConstant = _aReadTotalTimeoutConstant;
iCTO.ReadTotalTimeoutMultiplier = _aReadTotalTimeoutMultiplier;
iCTO.WriteTotalTimeoutConstant = _aWriteTotalTimeoutConstant;
iCTO.WriteTotalTimeoutMultiplier = _aWriteTotalTimeoutMultiplier; return SetTimeouts( iCTO );
}// 利用COMMTIMEOUTS结构体,获取延时设置
bool CSerialPort::GetTimeouts(COMMTIMEOUTS& _aCO)
{
JudgePortInfo();
return GetCommTimeouts( ihCommHandle, & _aCO ) == TRUE;
}bool CSerialPort::InitIOBuffer(DWORD _aInBufferSize, DWORD _aOutBufferSize)
{
iInputBuffSize = _aInBufferSize;
iOutputBuffSize = _aOutBufferSize;
return SetupComm( ihCommHandle, _aInBufferSize, _aOutBufferSize ) ==TRUE;
}// 设置接缓冲区大小
bool CSerialPort::SetInputBuffer(unsigned long _aInLength)
{
JudgePortInfo();
if ( InitIOBuffer( _aInLength, iOutputBuffSize ) )
{
iInputBuffSize = _aInLength;
return true;
}
return false;
}//接三楼
//接二楼// 设置发送缓冲区大小
bool CSerialPort::SetOutoutBuffer(unsigned long _aOutLength)
{
JudgePortInfo(); if( InitIOBuffer( iInputBuffSize, _aOutLength ) )
{
iOutputBuffSize = _aOutLength;
return true;
}
return false;
}// 写数据
unsigned long CSerialPort::SendData(LPVOID _alpBuff, DWORD _aToWriteLength)
{
JudgePortInfo(); InitOverLapped(); //必须设置
assert( iWriteOverLapped.hEvent != NULL );
DWORD _aWrittenLength = 0;
DWORD _tError = 0;
COMSTAT iState;
ClearCommError( ihCommHandle, &_tError, &iState );
if( 0 == GetLastError() && _tError > 0 )
PurgeComm( ihCommHandle, PURGE_TXABORT | PURGE_TXCLEAR );
string _tstr((char *)_alpBuff);
if( !WriteFile( ihCommHandle ,
_alpBuff,
_tstr.length(),
& _aWrittenLength,
&iWriteOverLapped )
)
{
if(::GetLastError() != ERROR_IO_PENDING)
{
_aWrittenLength = 0;
return _aWrittenLength;
}
}
if ( ERROR_IO_PENDING == GetLastError() ) //Send Data Successful
{
_aWrittenLength = 0;
return _aWrittenLength;
}
return _aWrittenLength;
}
// 发送任意数据
unsigned long CSerialPort::SendData(char* _awBuff, unsigned long _aToWriteNum, char* _awFormat, ...)
{
JudgePortInfo(); va_list va;
va_start(va, _awFormat);
_vsnprintf(_awBuff, _aToWriteNum, _awFormat, va);
va_end(va); return SendData( _awBuff, _aToWriteNum );
}// 清除发送缓冲区
bool CSerialPort::ClearOutputBuffer(void)
{
if ( IsPortOpen() )
return PurgeComm( ihCommHandle, PURGE_TXABORT | PURGE_TXCLEAR ) == TRUE;
return false;
}// 清除接收缓冲区
bool CSerialPort::ClearInputBuffer(void)
{
if( IsPortOpen() )
return PurgeComm( ihCommHandle, PURGE_RXABORT | PURGE_RXCLEAR ) == TRUE;
return false;
}void CSerialPort::InitOverLapped(void)
{
memset( &iWriteOverLapped, 0, sizeof( iWriteOverLapped ) );
memset( &iReadOverLapped, 0, sizeof( iWriteOverLapped ) );
iWriteOverLapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
iReadOverLapped.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
}// 接收数据
unsigned long CSerialPort::RecvData(void* rcvBuf, long _aNumberOfBytesToRead, unsigned long& _aNumberOfBytesRead)
{
JudgePortInfo( );
DWORD _tError;
if(ClearCommError( ihCommHandle, &_tError, &iState) && _tError > 0)
{
PurgeComm( ihCommHandle, PURGE_RXABORT | PURGE_RXCLEAR );
return 0;
} if(!iState.cbInQue) // 缓冲区无数据
return 0;
_aNumberOfBytesToRead = _aNumberOfBytesToRead > iState.cbInQue ? iState.cbInQue : _aNumberOfBytesToRead;
if(!ReadFile( ihCommHandle, rcvBuf, _aNumberOfBytesToRead, &_aNumberOfBytesRead, &iReadOverLapped ) )
{
if( ERROR_IO_PENDING == GetLastError() )
{
if(!GetOverlappedResult( ihCommHandle, &iReadOverLapped, &_aNumberOfBytesRead, false))
{
if( GetLastError() != ERROR_IO_INCOMPLETE )
_aNumberOfBytesRead = 0;
}
}
else
_aNumberOfBytesRead = 0;
}
return _aNumberOfBytesRead;
}// 该函数如果检测,当前窗口未打开,或被别的程序占用,则抛出异常!
void CSerialPort::JudgePortInfo(void)
{
if ( ihCommHandle == INVALID_HANDLE_VALUE )
throw exception("串口未打开,或被其他的应用程序占用!" );
}//若发送缓冲区空 或 接收缓冲区中有一个字符到来,都发送一个消息
DWORD WINAPI CSerialPort::WatchIOEvent(LPVOID _alpParameter)
{
CSerialPort _tSPort = *( CSerialPort * ) _alpParameter;
COMMTIMEOUTS _tCO;
_tCO.ReadIntervalTimeout = 1;
_tCO.ReadTotalTimeoutConstant = 10;
_tCO.ReadTotalTimeoutMultiplier = 10;
_tCO.WriteTotalTimeoutConstant = 10;
_tCO.WriteTotalTimeoutConstant = 10;
_tCO.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts ( _tSPort.ihCommHandle, & _tCO );
cout << "Set timeouts error = " << GetLastError () << endl;
char rcvbuf[ 2 ]; //从接收缓冲区中提取一个数据
_tSPort.iReadOverLapped.hEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL );
cout << "Create Error " << GetLastError() << endl;
if( _tSPort.iReadOverLapped.hEvent == NULL )
throw exception( "异常,获取串口事件异常!" );
DWORD _tError;
BOOL WaitEvt = FALSE;
BOOL ReadSuccess = FALSE;
DWORD ReceivedNum = 0;
ClearCommError ( _tSPort.ihCommHandle, &_tError,&_tSPort.iState );
cout << "Clear Error " << GetLastError() << endl;
if( !SetCommMask( _tSPort.ihCommHandle, EV_RXCHAR | EV_TXEMPTY ) )
throw exception( "异常,设置监视串口事件组的行为失败!" );
GetCommState ( _tSPort.ihCommHandle, &_tSPort.iDCB );
while( TRUE )
{
::WaitForSingleObject ( _tSPort.iReadOverLapped.hEvent , INFINITE );
//在这之间进行读操作
WaitEvt = WaitCommEvent( _tSPort.ihCommHandle, &_tSPort.iEvtMask, &_tSPort.iReadOverLapped );
cout << "Wait Error = "<< GetLastError() << endl;
if( WaitEvt )
{
//更新串口状态结构体,并清除所有串口硬件错误
::ClearCommError( _tSPort.ihCommHandle , &_tError, &_tSPort.iState );
cout << "Clear Error = "<< GetLastError() << endl;
}
else if( !WaitEvt && GetLastError()==ERROR_IO_PENDING )
{
//更新串口状态结构体,并清除所有串口硬件错误
ClearCommError( _tSPort.ihCommHandle, &_tError, &_tSPort.iState);
cout << "Clear Error = "<< GetLastError() << endl; while ( _tSPort.iState.cbInQue > 0 )
{
ReadSuccess = ReadFile ( _tSPort.ihCommHandle,
(LPVOID) rcvbuf,
1,
&ReceivedNum,
&_tSPort.iReadOverLapped
);
cout << "Read Error = "<<GetLastError() << endl; if ( !ReadSuccess && GetLastError() == ERROR_IO_PENDING )
{
while(GetOverlappedResult( _tSPort.ihCommHandle,
&_tSPort.iReadOverLapped,
&ReceivedNum,
TRUE
)
) {
_tError=GetLastError();
if ( _tError == ERROR_IO_INCOMPLETE )
continue;
else
break;
}
}
//更新串口状态结构体,并清除所有串口硬件错误
ClearCommError(_tSPort.ihCommHandle,&_tError,&_tSPort.iState);
}
}
::ReleaseMutex( _tSPort.iReadOverLapped.hEvent );
}
return 0;
}
原来是rcvBuf没有分配空间所致。
利用new分配符,rcv分配空间,就能搞定!