?? serialcomm.cpp
字號:
// SerialComm.cpp: implementation of the CSerialComm class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SerialComm.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSerialComm::CSerialComm()
{
#if !defined(SC_MSCOMOBJECT)
m_bAsyncOutput = FALSE; //同步寫方式
m_hCOM = NULL; //串行通訊端口句柄
::ZeroMemory(&m_stDCB, sizeof(DCB));
::ZeroMemory(&m_stCTO, sizeof(COMMTIMEOUTS));
::ZeroMemory(m_atcCurCOM, sizeof(TCHAR) * SC_MAXCOMNAME);
::ZeroMemory(&m_stROverlapped, sizeof(m_stROverlapped));
::ZeroMemory(&m_stWOverlapped, sizeof(m_stWOverlapped));
m_stWOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_stROverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
m_bShowCommConfigDialog = FALSE;
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
CSerialComm::~CSerialComm()
{
Close();
#if !defined(SC_MSCOMOBJECT)
if (NULL != m_stROverlapped.hEvent)
{
::CloseHandle(m_stROverlapped.hEvent);
}
if (NULL != m_stWOverlapped.hEvent)
{
::CloseHandle(m_stWOverlapped.hEvent);
}
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//獲取當(dāng)前系統(tǒng)中安裝的串行通訊端口
//<參數(shù)>
// pppCOMs: 用于接收一個(gè)指向串行通訊端口名集的指針
//<返回值>
// 返回當(dāng)前系統(tǒng)中安裝的串行通訊端口名集。
int CSerialComm::GetCOMs(LPCTSTR ** pppCOMs)
{
#if !defined(SC_MSCOMOBJECT)
if (NULL == pppCOMs)
return int(0);
static TCHAR atcCOMs[SC_MAXCOMNUM][SC_MAXCOMNAME];
static LPTSTR apCOMs[SC_MAXCOMNUM];
::ZeroMemory(atcCOMs, sizeof(TCHAR) * SC_MAXCOMNUM * SC_MAXCOMNAME);
int iCount = 0;
HANDLE hCOM = NULL;
for (int i = 0; i < SC_MAXCOMNUM; i++)
{
apCOMs[i] = atcCOMs[i];
::_stprintf(atcCOMs[iCount], _T("COM%d"), i+1);
//打開串口
hCOM = ::CreateFile(atcCOMs[iCount],
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
//如果串口打開失敗...
if (INVALID_HANDLE_VALUE == hCOM || NULL == hCOM)
{
DWORD dwLastError = ::GetLastError();
if (ERROR_FILE_NOT_FOUND != dwLastError)
++iCount;
else
::ZeroMemory(atcCOMs[iCount], sizeof(TCHAR) * SC_MAXCOMNAME);
}
else
{
::CloseHandle(hCOM);
++iCount;
}
}
if (0 < iCount)
*pppCOMs = (LPCTSTR *)apCOMs;
return int(iCount);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//打開指定串行通訊端口并指定其波特率和校驗(yàn)方式
//<參數(shù)>
// lpszCOMName: 指定端口名稱
// dwBaudrate: 指定波特率
// dwParity: 指定校驗(yàn)方式
//<返回值>
// 如果成功返回TRUE,否則返回FALSE。
BOOL CSerialComm::Open(LPCTSTR lpszCOMName, DWORD dwBaudrate/* = 0xFFFFFFFFl*/, DWORD dwParity/* = 0xFFFFFFFFl*/)
{
#if !defined(SC_MSCOMOBJECT)
Close();
if (NULL == lpszCOMName || FALSE != m_bShowCommConfigDialog)
return BOOL(FALSE);
//打開串口
m_hCOM = ::CreateFile(lpszCOMName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
//如果串口打開失敗...
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
{
m_hCOM = NULL;
return BOOL(FALSE);
}
//設(shè)置缺省配置
SetDefaultConfig(dwBaudrate, BYTE(dwParity));
::lstrcpyn(m_atcCurCOM, lpszCOMName, SC_MAXCOMNAME - 1);
return BOOL(TRUE);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//關(guān)閉當(dāng)前使用的串行端口
//<參數(shù)> 無
//<返回值> 無
void CSerialComm::Close()
{
#if !defined(SC_MSCOMOBJECT)
if (NULL != m_hCOM)
{
::PurgeComm(m_hCOM, PURGE_RXABORT | PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
::CloseHandle(m_hCOM);
m_hCOM=NULL;
::ZeroMemory(m_atcCurCOM, sizeof(TCHAR) * SC_MAXCOMNAME);
}
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//判斷當(dāng)前串行通訊端口是否已經(jīng)打開
//<參數(shù)> 無
//<返回值>
// 如果已經(jīng)打開則返回已打開串口的名字,否則返回NULL。
LPCTSTR CSerialComm::IsOpened()
{
#if !defined(SC_MSCOMOBJECT)
return LPCTSTR((NULL != m_hCOM && INVALID_HANDLE_VALUE != m_hCOM) ? m_atcCurCOM : NULL);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
#if !defined(SC_MSCOMOBJECT)
//設(shè)置當(dāng)前串行通訊端口的缺省配置
//<參數(shù)>
// dwBaudrate: 指定波特率,如果為0xFFFFFFFF表示保持原有設(shè)置值
// ucParity: 指定校驗(yàn)方式,如果為0xFFFF表示保持原有設(shè)置值
//<返回值> 無
void CSerialComm::SetDefaultConfig(DWORD dwBaudrate, BYTE ucParity)
{
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
return;
if (FALSE != ::GetCommState(m_hCOM, &m_stDCB))
{
if (0xFFFFFFFF != dwBaudrate)
m_stDCB.BaudRate = FormatBaudrate(dwBaudrate);
if (0xFF != ucParity)
{
m_stDCB.Parity = FormatParity(ucParity);
m_stDCB.fParity = BOOL(m_stDCB.Parity != NOPARITY);
}
m_stDCB.DCBlength = sizeof(DCB); //DCB數(shù)據(jù)結(jié)構(gòu)長度
m_stDCB.ByteSize = 8; //數(shù)據(jù)位
m_stDCB.StopBits = ONESTOPBIT; //停止位
m_stDCB.fAbortOnError = FALSE; //發(fā)生通訊錯(cuò)誤時(shí)不終止讀寫
m_stDCB.fBinary = TRUE; //以二近制方式通訊
::SetCommState(m_hCOM, &m_stDCB);
}
if (FALSE != ::GetCommTimeouts(m_hCOM, &m_stCTO))
{
int iBits = 1 + m_stDCB.ByteSize + ((m_stDCB.StopBits == ONESTOPBIT) ? 1 : 2);
m_stCTO.ReadIntervalTimeout = 10; //10ms的區(qū)間超時(shí)
m_stCTO.ReadTotalTimeoutMultiplier = (iBits * 1000) / m_stDCB.BaudRate; //傳輸一個(gè)字節(jié)所需的時(shí)間
if (0 == m_stCTO.ReadTotalTimeoutMultiplier)
m_stCTO.ReadTotalTimeoutMultiplier = 1;
m_stCTO.ReadTotalTimeoutConstant = m_stCTO.ReadTotalTimeoutMultiplier * 10;
m_stCTO.WriteTotalTimeoutMultiplier = ((iBits + 2) * 1000) / m_stDCB.BaudRate; //傳輸一個(gè)字節(jié)所需的時(shí)間
if (0 == m_stCTO.WriteTotalTimeoutMultiplier)
m_stCTO.WriteTotalTimeoutMultiplier = 1;
m_stCTO.WriteTotalTimeoutConstant = m_stCTO.WriteTotalTimeoutMultiplier * 10;
::SetCommTimeouts(m_hCOM, &m_stCTO);
}
}
#endif //if !defined(SC_MSCOMOBJECT)
//從當(dāng)前串行通訊端口讀取數(shù)據(jù)
//<參數(shù)>
// pBuff: 指定用于接收數(shù)據(jù)的緩沖區(qū)
// lReadSize: 指定期望讀取的數(shù)據(jù)長度
//<返回值>
// 返回正確讀取的字節(jié)數(shù)。如果指定一個(gè)NULL緩沖區(qū)或期望讀取的數(shù)據(jù)長度不大于0,
//則返回當(dāng)前系統(tǒng)內(nèi)部讀緩沖區(qū)中的字節(jié)數(shù)。出錯(cuò)時(shí)返回值小于0。
long CSerialComm::Read(LPVOID pBuff, long lReadSize)
{
#if !defined(SC_MSCOMOBJECT)
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
return long(-1);
//如果入口參數(shù)無效則試圖返回輸入緩沖區(qū)中的數(shù)據(jù)長度
if (NULL == pBuff || 0 > lReadSize)
{
COMSTAT stCOMStat;
DWORD dwErr;
if (FALSE == ::ClearCommError(m_hCOM, &dwErr, &stCOMStat))
return long(-2);
return long(stCOMStat.cbInQue);
}
DWORD dwReadedSize = 0;
long lReaded = 0;
int iCount = (lReadSize + 3) >> 2;
while (iCount > 0)
{
--iCount;
::ResetEvent(m_stROverlapped.hEvent);
//如果讀取失敗...
if (FALSE == ::ReadFile(m_hCOM, LPBYTE(pBuff)+lReaded, lReadSize, &dwReadedSize, &m_stROverlapped))
{
//如果是重疊IO...
if (ERROR_IO_PENDING == GetLastError())
{
if (FALSE != ::GetOverlappedResult(m_hCOM, &m_stROverlapped, &dwReadedSize, TRUE))
{
lReaded += long(dwReadedSize);
lReadSize -= long(dwReadedSize);
if (0 >= lReadSize || 0 == dwReadedSize)
return long(lReaded);
else
{
iCount = min((lReadSize + 3) >> 2, iCount);
}
}
} //if (ERROR_IO_PENDING == GetLastError())
} //if (FALSE == ::ReadFile)
else
{
lReaded += long(dwReadedSize);
//讀取完成
return long(lReaded);
}
} //while(dwSize)
return long(lReaded);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//向當(dāng)前串行通訊端口寫數(shù)據(jù)
//<參數(shù)>
// pData: 指定待寫數(shù)據(jù)
// lWriteSize: 指定待寫數(shù)據(jù)長度
//<返回值>
// 返回正確寫出的字節(jié)數(shù)。如果指定pData為NULL或待寫數(shù)據(jù)長度不大于0,
//則返回當(dāng)前系統(tǒng)內(nèi)部寫緩沖區(qū)中的字節(jié)數(shù)。出錯(cuò)時(shí)返回值小于0。
long CSerialComm::Write(LPVOID pData, long lWriteSize)
{
#if !defined(SC_MSCOMOBJECT)
if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
return long(-1);
COMSTAT stCOMStat;
DWORD dwErr = 0;
//如果入口參數(shù)無效則試圖返回輸入緩沖區(qū)中的數(shù)據(jù)長度
if (NULL == pData || 0 > lWriteSize)
{
if (FALSE == ::ClearCommError(m_hCOM, &dwErr, &stCOMStat))
return long(-2);
return long(stCOMStat.cbOutQue);
}
DWORD dwWritedSize = 0;
long lWritedTatol = 0;
if (FALSE != m_bAsyncOutput)
{
::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE);
dwWritedSize = 0;
}
while (0 < lWriteSize)
{
::ResetEvent(m_stWOverlapped.hEvent);
//如果發(fā)送失敗...
if (FALSE == ::WriteFile(m_hCOM, LPBYTE(pData) + lWritedTatol, DWORD(lWriteSize), &dwWritedSize, &m_stWOverlapped))
{
//如果是重疊IO...
if (ERROR_IO_PENDING == ::GetLastError())
{
//如果重疊IO失敗...
if (FALSE == ::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE))
{
if (FALSE != m_bAsyncOutput)
return long(lWritedTatol);
DWORD dwWait = m_stCTO.WriteTotalTimeoutMultiplier * lWriteSize + m_stCTO.WriteTotalTimeoutConstant;
if (WAIT_TIMEOUT == ::WaitForSingleObject(m_stWOverlapped.hEvent, dwWait))
return long(-3);
if (FALSE == ::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE))
return long(-4);
}
if (0 == dwWritedSize)
return long(lWritedTatol);
lWritedTatol += long(dwWritedSize);
lWriteSize -= long(dwWritedSize);
} //if (ERROR_IO_PENDING == ::GetLastError())
else
return long(-5);
}
else
{
lWritedTatol += long(dwWritedSize);
lWriteSize -= long(dwWritedSize);
}
}
if (FALSE != m_bAsyncOutput)
return long(lWritedTatol);
//清空軟件緩沖區(qū)
::FlushFileBuffers(m_hCOM);
DCB stDCB;
if (FALSE != ::GetCommState(m_hCOM, &stDCB))
{
int iBits = 1 + stDCB.ByteSize + ((stDCB.StopBits == ONESTOPBIT) ? 1 : 2); //一個(gè)字節(jié)的實(shí)際傳輸位數(shù)
int iBytesTime = (iBits * 1000 * min(16 + 2, dwWritedSize + 2)) / stDCB.BaudRate; //傳輸若干字節(jié)所需要的時(shí)間
::Sleep(iBytesTime); //等待硬件緩沖區(qū)空
}
return long(lWritedTatol);
#else //if !defined(SC_MSCOMOBJECT)
#endif
}
//對Win32API CommConfigDialog函數(shù)的封裝
//<參數(shù)> 無
//<返回值>
// 如果成功返回TRUE,否則返回FALSE。
BOOL CSerialComm::CommConfigDialog()
{
#if !defined(SC_MSCOMOBJECT)
if (NULL != IsOpened())
{
DWORD dwCCSize;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -