📄 serialport.cpp
字号:
// SerialPort.cpp: implementation of the CSerialPort class.
#include "stdafx.h" //这一行让人生气, 如果没有,VC6里如果使用MFC将不能编译
#include "SerialPort.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
DWORD WINAPI CSerialPort::MonitorCommThreadProc(LPVOID lpParameter)
{
static OVERLAPPED ov_Read; //异步读串口时用到的重叠结构
ov_Read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
volatile LPMONITORCOMMPARAMS lpParam;
if (NULL == lpParameter)
{
return -1;
}
lpParam = (LPMONITORCOMMPARAMS)lpParameter;
if ( (lpParam->hCommPort) == INVALID_HANDLE_VALUE)
{
ProcessErrorMessage(TEXT("串口句柄无效,串口监控线程无法工作."));
return -1;
}
// 清空串口缓冲区, 退出所有相关操作
PurgeComm( lpParam->hCommPort,
PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
COMSTAT ComStat; // 串口状态
DWORD dwEvent, dwError, dwResult;
// 进入无限循环, 直到线程被关闭
while ( !(lpParam->bTerminated) )
{
//调用WaitCommEvent().这一调用将立即返回,因为用异步方式
//(FILE_FLAG_OVERLAPPED)打开串口,并且指定重叠操作结构,
//如果有一个字符到达串口,重叠结构中的事件将被置为有信号态
BOOL bResult = WaitCommEvent( (lpParam->hCommPort),
&dwEvent,
&ov_Read );
if (!bResult)
{
// 如果WaitCommEvent()返回FALSE,调用GetLastError()判断原因
dwError = GetLastError();
switch(dwError)
{
case ERROR_IO_PENDING: //重叠操作正在后台进行
// 如果串口这时无字符,这是正常返回值,继续
break;
case 87:
//在WIN NT下这是一个可能结果,但我没有找到
//它出现的原因,我什么也不做,继续
break;
default:
//所有其它错误代码均显示串口出错,显示出错信息
ProcessErrorMessage(TEXT("等待串口事件"));
return -1; // return; ?????????????????????????????
}
}
else
{
//如果WaitCommEvent()返回true,检查输入缓冲区是否确实有字节可读,
//若没有,则继续下一循环
//ClearCommError()将更新串口状态结构并清除所有串口硬件错误
ClearCommError( (lpParam->hCommPort), &dwError, &ComStat);
if (ComStat.cbInQue == 0) //输入缓冲队列长为0,无字符
{
continue;
}
}
// 等待串口事件产生
dwResult = WaitForSingleObject( ov_Read.hEvent, 1000); //???????????
if (dwResult == WAIT_OBJECT_0) // 事件产生
{
// 获取串口事件掩码
GetCommMask( (lpParam->hCommPort), &dwEvent );
if (dwEvent & EV_RXCHAR) // 有一个字符
{
CSerialPort::ReceiveAChar(lpParam, &ov_Read); //接收一个字符
}
}
}
CloseHandle(ov_Read.hEvent);
return 0;
}
void CSerialPort::ReceiveAChar(LPMONITORCOMMPARAMS lpParameter, LPOVERLAPPED lpOv)
{
COMSTAT ComStat;
DWORD dwError = 0;
BOOL bResult;
DWORD BytesRead = 0;
char ucRxBuff;
if (lpParameter == NULL || lpParameter->hCommPort == NULL)
{
return;
}
//开始无限循环,因为我不知到需要循环多少次.我的解决方法是开始一个无限循环,
//当我已处理了所有的有效数据后才退出.使用这种方法应小心的保证您的程序能
//正常退出循环.即便如此,我仍觉得这是最有效的解决办法
while(TRUE)
{
// ClearCommError()将更新串口状态结构并清除所有串口硬件错误
ClearCommError( (lpParameter->hCommPort), &dwError, &ComStat);
if (ComStat.cbInQue == 0)
{
// 缓冲区中已无数据, 退出循环
break;
}
// 读串口
bResult = ReadFile( (lpParameter->hCommPort),
&ucRxBuff,
1,
&BytesRead,
lpOv);
TRACE(_T("bResult = %d\n"), bResult);
if (IsNT())
{
bResult = GetOverlappedResult( (lpParameter->hCommPort),
lpOv, &BytesRead, TRUE);
if ( ! bResult )
{
ProcessErrorMessage(TEXT("ReceiveAChar(): GetOverlappedResult() failed"));
return ;
}
}
else if (!bResult)
{
switch(dwError = GetLastError())
{
case ERROR_IO_PENDING: // 重叠操作在后台进行
BytesRead = 0;
// 等待重叠操作结果
bResult = GetOverlappedResult( (lpParameter->hCommPort),//串口句柄
lpOv, //重叠结构
&BytesRead, //实际读入字符数
TRUE); //等待直到串口操作超时
if (!bResult) // 重叠操作失败
{
ProcessErrorMessage(TEXT("(读串口时)获取重叠操作结果"));
return;
}
break;
default:
ProcessErrorMessage(TEXT("Read SerialPort"));
return;
}
}
if (BytesRead != 1)
{
continue;
}
// 将收到的字符当作消息的参数投递到父窗口
#ifndef UNICODE
PostMessage( (lpParameter->hwndParent), WM_COMM_RXCHAR, (WPARAM)ucRxBuff, NULL);
#else
union tagRutBuff
{
WORD wBuffer;
BYTE byteBuffer[2];
DWORD dwDummy;
};
static tagRutBuff uReturn;
if (uReturn.byteBuffer[0] == '\0' )
{
uReturn.byteBuffer[0] = ucRxBuff;
if ( uReturn.byteBuffer[0] < 0x80 ) // 这里有 BUG , 0x80 这个值有待修正
{
PostMessage((lpParameter->hwndParent),
WM_COMM_RXCHAR,
(WPARAM)uReturn.wBuffer,
NULL);
uReturn.wBuffer = 0;
}
}
else
{
uReturn.byteBuffer[1] = ucRxBuff;
int iLength = MultiByteToWideChar( CP_ACP,
0,
(LPCSTR)(&(uReturn.byteBuffer[0])),
2,
NULL,
0);
PWSTR pWideStr = (PWSTR)malloc(iLength * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP,
0,
(LPCSTR)(&(uReturn.byteBuffer[0])),
2,
pWideStr,
iLength);
PostMessage((lpParameter->hwndParent),
WM_COMM_RXCHAR,
(WPARAM)(*pWideStr),
NULL);
free(pWideStr);
uReturn.wBuffer = 0;
}
#endif // UNICODE
}
}
CSerialPort::CSerialPort()
{
m_hComm = INVALID_HANDLE_VALUE; // 串口句柄
m_hMonitorThread = INVALID_HANDLE_VALUE; // 监控线程句柄
m_ov_Write.hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT(""));
m_hwndParent = NULL;
// 线程过程的参数清零
memset(&m_Param, 0, sizeof(m_Param));
}
CSerialPort::~CSerialPort()
{
m_Param.bTerminated = TRUE; //告诉监控线程结束循环
StartMonitoring(); // 如果监控线程已经挂起, 则在结束它之前启动它.
if (m_ov_Write.hEvent)
{
CloseHandle(m_ov_Write.hEvent);
m_ov_Write.hEvent = NULL;
}
if (m_hMonitorThread != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hMonitorThread);
m_hMonitorThread = NULL;
}
if (m_hComm != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hComm);
}
}
void CSerialPort::ProcessErrorMessage(LPTSTR ErrorText)
{
TCHAR ErrorMsg[400];
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL,GetLastError(), //获取错误信息标识
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),//使用系统缺省语言
(LPTSTR)&lpMsgBuf, //消息缓冲区
0,
NULL);
wsprintf(ErrorMsg, TEXT("\"%s\" 由于以下错误而失败: \n\n%s"),ErrorText,lpMsgBuf);
MessageBox(NULL/*m_hwndParent*/, ErrorMsg, TEXT("串口错误"), MB_ICONSTOP);
LocalFree(lpMsgBuf);
}
//启动串口监控线程
void CSerialPort::StartMonitoring()
{
ResumeThread(m_hMonitorThread);
}
//挂起线程
void CSerialPort::StopMonitoring()
{
SuspendThread(m_hMonitorThread);
}
//向串口写一个字符
BOOL CSerialPort::WriteToPort(TCHAR ucTxChar)
{
DWORD iLength = 1;
if (m_hComm == INVALID_HANDLE_VALUE || m_hComm == NULL)
{
MessageBox(m_hwndParent,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -