📄 serialport.cpp
字号:
//=======================================================================================
// Module : SERIALPORT.CPP
// Purpose: Implementation for an MFC wrapper class for serial ports
// Created: PJN / 31-05-1999
//=======================================================================================
//=======================================================================================
// Includes
#include "stdafx.h"
#include "serialport.h"
#ifndef _WINERROR_
#include "winerror.h"
#endif
//=======================================================================================
// defines
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//=======================================================================================
// Implementation
//---------------------------------------------------------------------------------------
// Class which handles CancelIo function which must be constructed at run time since it
// is not imeplemented on NT 3.51 or Windows 95. To avoid the loader bringing up a
// message such as "Failed to load due to missing export...", the function is constructed
// using GetProcAddress. The CSerialPort::CancelIo function then checks to see if the
// function pointer is NULL and if it is it throws an exception using the error code
// ERROR_CALL_NOT_IMPLEMENTED which is what 95 would have done if it had implemented
// a stub for it in the first place.
//---------------------------------------------------------------------------------------
class _SERIAL_PORT_DATA
{
public:
//Constructors /Destructors
_SERIAL_PORT_DATA();
~_SERIAL_PORT_DATA();
HINSTANCE m_hKernel32;
typedef BOOL (WINAPI CANCELIO) (HANDLE);
typedef CANCELIO* LPCANCELIO;
LPCANCELIO m_lpfnCancelIo;
};
_SERIAL_PORT_DATA::_SERIAL_PORT_DATA()
{
#ifdef UNDER_CE
// CE 下根本就不支持
m_hKernel32 = NULL;
m_lpfnCancelIo = NULL;
#else
m_hKernel32 = LoadLibrary( _T("KERNEL32.DLL") );
VERIFY( m_hKernel32 != NULL );
m_lpfnCancelIo = (LPCANCELIO) GetProcAddress( m_hKernel32, "CancelIo" );
#endif
}
_SERIAL_PORT_DATA::~_SERIAL_PORT_DATA()
{
#ifndef UNDER_CE
FreeLibrary(m_hKernel32);
m_hKernel32 = NULL;
#endif
}
// The local variable which handle the function pointers
static _SERIAL_PORT_DATA _SerialPortData;
//***************************************************************************************
// Exception handling code
//***************************************************************************************
void AfxThrowSerialException(DWORD dwError /* = 0 */)
{
if (dwError == 0)
// 为 0 时表示出现系统错误,而不是我们所定义的
dwError = ::GetLastError();
CSerialException* pException = new CSerialException(dwError);
TRACE(_T("Warning: throwing CSerialException for error %d\n"), dwError);
THROW(pException);
}
//---------------------------------------------------------------------------------------
// GetErrorMessage 将错误消息拷贝到 pstrError 中,最大 nMaxError 个字符
//---------------------------------------------------------------------------------------
BOOL CSerialException::GetErrorMessage(LPTSTR pstrError, UINT nMaxError, PUINT pnHelpContext)
{
ASSERT(pstrError != NULL && AfxIsValidString(pstrError, nMaxError));
if (pnHelpContext != NULL)
*pnHelpContext = 0;
LPTSTR lpBuffer;
BOOL bRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, m_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
(LPTSTR) &lpBuffer, 0, NULL);
if (bRet == FALSE)
{
*pstrError = _T('\0');
}
else
{
_tcsncpy(pstrError, lpBuffer, nMaxError);
bRet = TRUE;
LocalFree(lpBuffer);
}
return bRet;
}
CString CSerialException::GetErrorMessage()
{
CString strVal;
LPTSTR pstrError = strVal.GetBuffer( 2048*sizeof(TCHAR) );
GetErrorMessage( pstrError, 2048, NULL );
strVal.ReleaseBuffer();
return strVal;
}
IMPLEMENT_DYNAMIC(CSerialException, CException)
#ifdef _DEBUG
void CSerialException::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "m_dwError = " << m_dwError;
}
#endif
//***************************************************************************************
// The actual serial port code
//***************************************************************************************
CSerialPort::CSerialPort()
{
m_hComm = INVALID_HANDLE_VALUE;
m_bOverlapped = FALSE;
m_hEvent = NULL;
}
CSerialPort::~CSerialPort()
{
Close();
}
IMPLEMENT_DYNAMIC(CSerialPort, CObject)
#ifdef _DEBUG
void CSerialPort::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << _T("m_hComm = ") << m_hComm << _T("\n");
dc << _T("m_bOverlapped = ") << m_bOverlapped;
}
#endif
//---------------------------------------------------------------------------------------
// 串口打开操作
//---------------------------------------------------------------------------------------
void CSerialPort::Open(int nPort, DWORD dwBaud, Parity parity, BYTE DataBits,
StopBits stopbits, FlowControl fc, BOOL bOverlapped)
{
// Validate our parameters
ASSERT(nPort>0 && nPort<=255);
Close(); // In case we are already open
// Call CreateFile to open up the comms port
CString strPort;
#ifdef UNDER_CE
// 注意 ':' 号
strPort.Format(_T("COM%d:"), nPort);
#else
strPort.Format(_T("\\\\.\\COM%d"), nPort);
#endif
// CE 下根本就不支持重叠操作,因此始终为 FALSE
#ifdef UNDER_CE
bOverlapped = FALSE;
#endif
DWORD dwCreateProperty;
if(bOverlapped)
{
dwCreateProperty = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
}
else
{
dwCreateProperty = FILE_ATTRIBUTE_NORMAL;
}
// bOverlapped ? FILE_FLAG_OVERLAPPED : 0
// Open the serial port.
m_hComm = CreateFile(strPort, // Pointer to the name of the port
GENERIC_READ | GENERIC_WRITE,
// Access (read-write) mode
0, // Share mode
NULL, // Pointer to the security attribute
OPEN_EXISTING, // How to open the serial port
dwCreateProperty, // Port attributes
NULL); // Handle to port with attribute to copy
if (m_hComm == INVALID_HANDLE_VALUE)
{
TRACE(_T("Failed to open up the comms port!\n"));
AfxThrowSerialException();
}
m_CurPortNum = nPort;
/*
// Create the event we need for later synchronisation use
if(bOverlapped)
{
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
Close();
TRACE(_T("Failed in call to CreateEvent in Open\n"));
AfxThrowSerialException();
}
}
*/
m_bOverlapped = bOverlapped;
// Get the current state prior to changing it
DCB dcb;
dcb.DCBlength = sizeof(DCB);
GetState(dcb);
//-------------------------------------------------------------------------
// 设置串口属性
//-------------------------------------------------------------------------
// Setup the baud rate
dcb.BaudRate = dwBaud;
// Setup the Parity
switch (parity)
{
case EvenParity: dcb.Parity = EVENPARITY; break;
case MarkParity: dcb.Parity = MARKPARITY; break;
case NoParity: dcb.Parity = NOPARITY; break;
case OddParity: dcb.Parity = ODDPARITY; break;
case SpaceParity: dcb.Parity = SPACEPARITY; break;
default:
ASSERT(FALSE);
break;
}
// Setup the data bits
dcb.ByteSize = DataBits;
// Setup the stop bits
switch (stopbits)
{
case OneStopBit: dcb.StopBits = ONESTOPBIT; break;
case OnePointFiveStopBits: dcb.StopBits = ONE5STOPBITS; break;
case TwoStopBits: dcb.StopBits = TWOSTOPBITS; break;
default:
ASSERT(FALSE);
break;
}
// Setup the flow control
dcb.fDsrSensitivity = FALSE;
switch (fc)
{
case NoFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case CtsRtsFlowControl:
{
dcb.fOutxCtsFlow = TRUE;
dcb.fOutxDsrFlow = FALSE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case CtsDtrFlowControl:
{
dcb.fOutxCtsFlow = TRUE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case DsrRtsFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = TRUE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case DsrDtrFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = TRUE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
break;
}
case XonXoffFlowControl:
{
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutX = TRUE;
dcb.fInX = TRUE;
dcb.XonChar = 0x11;
dcb.XoffChar = 0x13;
dcb.XoffLim = 100;
dcb.XonLim = 100;
break;
}
default:
ASSERT(FALSE);
break;
}
dcb.fAbortOnError = FALSE; // Terminate Reads & Writes if there's an error
dcb.fErrorChar = TRUE; // Replace any garbled bytes with ErrorChar
dcb.ErrorChar = ' '; // Garbage bytes are spaces
dcb.fBinary = TRUE; // Ignore EOF
// Now that we have all the settings in place, make the changes
SetState(dcb);
}
void CSerialPort::Close()
{
if (!IsOpen())
return;
// Close down the comms port
BOOL bSuccess = CloseHandle(m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
if (!bSuccess)
{
TRACE(_T("Failed to close up the comms port, GetLastError:%d.\n"), GetLastError());
}
m_bOverlapped = FALSE;
// Free up the event object we are using
if(m_hEvent)
{
CloseHandle(m_hEvent);
m_hEvent = NULL;
}
}
void CSerialPort::Attach(HANDLE hComm, BOOL bOverlapped)
{
Close();
m_hComm = hComm;
m_bOverlapped = bOverlapped;
if( m_bOverlapped )
{
// Create the event we need for later synchronisation use
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
Close();
TRACE(_T("Failed in call to \"CreateEvent()\" in \"Attach()\".\n"));
AfxThrowSerialException();
}
}
}
HANDLE CSerialPort::Detach()
{
HANDLE hrVal = m_hComm;
m_hComm = INVALID_HANDLE_VALUE;
if( m_bOverlapped )
{
CloseHandle(m_hEvent);
m_hEvent = NULL;
}
return hrVal;
}
//---------------------------------------------------------------------------------------
// 读取 dwCount 个字节到 lpBuf 中,同步操作
// 没有读到任何数据或者读取数据过程中出错,返回 0
// 如果 dwCount 比 comstat.cbInQue,那么表示 lpBuf 足够大,可以放下待接收的数据
// 使用 comstat.cbInQue 为读取缓冲区大小
//---------------------------------------------------------------------------------------
DWORD CSerialPort::Read(void* lpBuf, DWORD dwCount)
{
ASSERT(IsOpen());
ASSERT(!m_bOverlapped);
DWORD errors;
COMSTAT comstat;
BOOL ok;
ok = ::ClearCommError(this->m_hComm, &errors, &comstat);
if( !ok )
{
return 0;
}
if( comstat.cbInQue == 0 )
{
return 0;
}
DWORD dwBytesRead = 0;
if( dwCount > comstat.cbInQue )
dwCount = comstat.cbInQue;
if (!ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, NULL))
{
TRACE(_T("Failed in call to \"ReadFile()\" in \"Read()\".\n"));
AfxThrowSerialException();
}
return dwBytesRead;
}
//---------------------------------------------------------------------------------------
// 读取 dwCount 个字节到 lpBuf 中,异步操作
// 读取完成或超时后,读取字节个数在 pBytesRead 中返回
//---------------------------------------------------------------------------------------
BOOL CSerialPort::Read(void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped,
DWORD* pBytesRead)
{
ASSERT(IsOpen());
ASSERT(m_bOverlapped);
DWORD dwBytesRead = 0;
BOOL bSuccess = ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, &overlapped);
if (!bSuccess)
{
if (GetLastError() != ERROR_IO_PENDING)
{
TRACE(_T("Failed in call to \"ReadFile()\" in \"Read()\".\n"));
AfxThrowSerialException();
}
}
else
{
if (pBytesRead)
*pBytesRead = dwBytesRead;
}
return bSuccess;
}
//---------------------------------------------------------------------------------------
// 将 lpBuf 中的 dwCount 个字节写入串口中,同步操作
// 返回实际写入的字节数
//---------------------------------------------------------------------------------------
DWORD CSerialPort::Write(const void* lpBuf, DWORD dwCount)
{
ASSERT(IsOpen());
ASSERT(!m_bOverlapped);
DWORD dwBytesWritten = 0;
if (!WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, NULL))
{
TRACE(_T("Failed in call to \"WriteFile()\" in \"Write()\".\n"));
AfxThrowSerialException();
}
return dwBytesWritten;
}
//---------------------------------------------------------------------------------------
// 将 lpBuf 中的 dwCount 个字节写入串口中,异步操作
// 实际写入的字节数在 pBytesWritten 中返回
//---------------------------------------------------------------------------------------
BOOL CSerialPort::Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped,
DWORD* pBytesWritten)
{
ASSERT(IsOpen());
ASSERT(m_bOverlapped);
DWORD dwBytesWritten = 0;
BOOL bSuccess = WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, &overlapped);
if (!bSuccess)
{
if (GetLastError() != ERROR_IO_PENDING)
{
TRACE(_T("Failed in call to \"WriteFile()\" in \"Write()\".\n"));
AfxThrowSerialException();
}
}
else
{
if (pBytesWritten)
*pBytesWritten = dwBytesWritten;
}
return bSuccess;
}
BOOL CSerialPort::GetOverlappedResult(OVERLAPPED& overlapped, DWORD& dwBytesTransferred, BOOL bWait)
{
#ifdef UNDER_CE
dwBytesTransferred = 0;
// TRACE(_T("Call unsupported function \"GetOverlappedResult()\"!\n"));
// AfxThrowSerialException(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
#else
ASSERT(IsOpen());
ASSERT(m_bOverlapped);
BOOL bSuccess = ::GetOverlappedResult(m_hComm, &overlapped, &dwBytesTransferred, bWait);
if (!bSuccess)
{
if (GetLastError() != ERROR_IO_PENDING)
{
TRACE(_T("Failed in call to \"GetOverlappedResult()\".\n"));
AfxThrowSerialException();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -