//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楼

解决方案 »

  1.   


    //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;
    }//接三楼
      

  2.   


    //接二楼// 设置发送缓冲区大小
    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;
    }
      

  3.   

    可怜呐。没人回答,只有你顶。不过谢你顶,我搞定了。
    原来是rcvBuf没有分配空间所致。
    利用new分配符,rcv分配空间,就能搞定!