MFC之路 串口通信篇(之二)
来源:互联网 发布:淘宝卖家开花呗条件 编辑:程序博客网 时间:2024/06/08 13:18
在前面一个章节的文章中,我们对串口进行了打开和参数的设置,接下来我们需要创建一个新的线程完成对串口的数据监听功能。
创建新的线程,一般分为两个部分,一个是创建一个线程,另一个就是创建线程的响应函数
1、首先,创建新的线程
接前面一节的程序代码:
//创建工作线程if(SetComParameterSucceed) //如果串口设置成功的话,接着创建新的线程{m_pThread=AfxBeginThread(ComProce,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL); //创建新线程函数,返回线程的指针if(m_pThread==NULL) //如果为NULL,表示创建失败{CloseHandle(m_hCom); //关闭前面创建的串口句柄AfxMessageBox(_T("线程创建失败!")); //弹出对话框提醒创建线程失败m_bConnected=0; //将连接标志置0return FALSE; //返回}else{m_pThread->ResumeThread(); //如果创建新线程成功了,调用线程恢复函数恢复线程}}else //如果串口设置没成功,直接返回{CloseHandle(m_hCom);AfxMessageBox(_T("参数设置失败!"));m_bConnected=0;return FALSE;}m_bConnected=1; //串口打开成功并且参数设置完成,而且新线程也创建成功之后,才将连接标志设置为1,否则如果有失败的情况前面已经返回FALSE了
还是对创建线程的过程中个别的函数进行特别说明:
首先是创建线程函数:
m_pThread=AfxBeginThread(ComProce,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL); //创建新线程函数,返回线程的指针
其中, CWinThread *m_pThread; //返回的新创建的监听线程指针。这个地方碰到一个问题,在声明线程响应函数ComProce时,将
UINT ComProce(LPVOID pParam); //串口监听线程的响应函数,这个定义只能放在这里,放在头文件中出错
放在头文件中是有错误的,必须放在源文件的最前面才行,我也不知道什么原因,希望有大神能够指教。
我们还是说一下创建线程函数的使用方法:
用户界面线程和工作者线程都是由AfxBeginThread创建的。MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程:
用户界面线程的AfxBeginThread的原型如下:CWinThread* AFXAPI AfxBeginThread( CRuntimeClass* pThreadClass, // 从CWinThread派生的RUNTIME_CLASS类; int nPriority, //指定线程优先级,如果为0,则与创建该线程的线程相同; UINT nStackSize, //指定线程的堆栈大小,如果为0,则与创建该线程的线程相同; DWORD dwCreateFlags, //一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。 LPSECURITY_ATTRIBUTES lpSecurityAttrs) //表示线程的安全属性,NT下有用
工作者线程的AfxBeginThread的原型如下:CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, //线程的入口函数,声明一定要如下: UINT MyThreadFunction(LPVOID pParam),不能设置为NULL; LPVOID lParam, //传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程. int nPriority = THREAD_PRIORITY_NORMAL, //线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级. UINT nStackSize = 0, //指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈 DWORD dwCreateFlags = 0, //指定创建线程以后,线程有怎么样的标志.可以指定两个值:CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用:ResumeThread0 : 创建线程后就开始运行. LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL //指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL,那么新创建的线程就具有和主线程一样的安全性. );//用于创建工作者线程返回值: 成功时返回一个指向新线程的线程对象的指针,否则NULL。
正是因为dwCreateFlags参数设置为了CREATE_SUSPENDED,即创建新线程后将其挂起,所以在后面接着调用了m_pThread->ResumeThread(); //如果创建新线程成功了,调用线程恢复函数恢复线程。
线程其他相关操作
1、线程的挂起
DWORD SuspendThread(HANDLE hThread)
返回值:成功则返回线程被挂起的次数;失败则返回0XFFFFFFFF。
2、线程的恢复
DWORD ResumeThread(HANDLE hTread)
返回值:成功则返回线程被挂起的次数;失败则返回0XFFFFFFFF。
3、要结束线程的两种方式
(1)、这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,
当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线程的一切资源都会被回收.注意在线程中使用了CString类,则不能用AfxEndThread来进行结束线程,会有内存泄漏,只有当程序结束时,会在输出窗口有提示多少byte泄漏了。因为Cstring的回收有其自己的机制。建议在AfxEndThread之前先进行return。
(2)、如果你想让另一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息。
不管是工作者线程还是界面线程,如果你想在线程结束后得到它的结果,那么你可以调用:
::GetExitCodeThread函数
2、创建新线程的响应函数
//串口线程响应函数UINT ComProce(LPVOID pParam){//AfxMessageBox("建立线程开始!");OVERLAPPED os;//重叠操作I/O结构体,一会详细介绍其作用DWORD dwMask,dwTrans; //无符号长整型,标志位COMSTAT ComStat; //包含串口信息的结构体DWORD dwErrorFlags; //错误标志位CSerialComSoftwareDlg *pDlg=(CSerialComSoftwareDlg *)pParam; //参数传入为this,即对话框类指针memset(&os,0,sizeof(OVERLAPPED)); //清空os结构体os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); //创建一个事件对象,将其赋值给os结构体if(os.hEvent==NULL) //创建事件失败{AfxMessageBox(_T("不能建立事件对象!"));return (UINT)-1;}//AfxMessageBox("已建立成功!");while(pDlg->m_bConnected) //如果串口通信已经连接{ClearCommError(pDlg->m_hCom,&dwErrorFlags,&ComStat); //清除硬件的通讯错误以及获取通讯设备的当前状态if(ComStat.cbInQue) //如果有数据到达{//AfxMessageBox("receive");pDlg->ProcessCOMMNotification(EV_RXCHAR,0); //串口有数据到达时调用此函数}dwMask=0; //没有数据时置0//AfxMessageBox("no data in!");if(!WaitCommEvent(pDlg->m_hCom,&dwMask,&os)) //为一个特指的通信设备等待一个事件发生,成功返回非0,失败返回0{//AfxMessageBox("wait event");if(GetLastError()==ERROR_IO_PENDING) //如果错误信息为ERROR_IO_PENDING,表示数据正在传输中{//AfxMessageBox("begin wait a data in!");GetOverlappedResult(pDlg->m_hCom,&os,&dwTrans,TRUE); //判断一个重叠操作的当前状态//AfxMessageBox("now,there is!");}else //如果错误信息为其他,说明通信出现问题,结束串口通信线程{ CloseHandle(os.hEvent);return(UINT)-1;}//AfxMessageBox("wait end");}}CloseHandle(os.hEvent); //线程结束,关闭事件//AfxMessageBox("线程结束!");return 0;}
有几个需要说明的地方:
第一个是,OVERLAPPED结构体,这个结构体中记录了串口操作的一些信息。
typedef struct _OVERLAPPED { DWORD Internal; //预留给操作系统使用 DWORD InternalHigh; //预留给操作系统使用 DWORD Offset; //该文件的位置是从文件起始处的字节偏移量。 DWORD OffsetHigh; //指定文件传送的字节偏移量的高位字 HANDLE hEvent; //在转移完成时处理一个事件设置为有信号状态 } OVERLAPPED
overlapped I/O是WIN32的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这项技术使你的程序在I/O进行过程中仍然能够继续处理事务。事实上,操作系统内部正是以线程来I/O完成overlapped I/O。你可以获得线程的所有利益,而不需付出什么痛苦的代价。
那么怎么设定对串口的操作是否采用OVERLAPPED的方式呢?使用CreateFile (),将其第6个参数指定为FILE_FLAG_OVERLAPPED,
就是准备使用overlapped的方式构造或打开文件,在我们前面的代码中正是应用了这种方式。
第二个是,结构体COMSTAT,这个结构体记录了串口的信息。
typedef struct _COMSTAT { // cst DWORD fCtsHold : 1; // Tx waiting for CTS signal DWORD fDsrHold : 1; // Tx waiting for DSR signal DWORD fRlsdHold : 1; // Tx waiting for RLSD signal DWORD fXoffHold : 1; // Tx waiting, XOFF char rec''d DWORD fXoffSent : 1; // Tx waiting, XOFF char sent DWORD fEof : 1; // EOF character sent DWORD fTxim : 1; // character waiting for Tx DWORD fReserved : 25; // reserved 保留 DWORD cbInQue; // bytes in input buffer该成员变量的值代表输入缓冲区的字节数 DWORD cbOutQue; // bytes in output buffer记录着输出缓冲区中字节数} COMSTAT, *LPCOMSTAT;
第三个是,CreateEvent()函数
os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); //创建一个事件对象,将其赋值给os结构体
CreateEvent是一个Windows API函数。它用来创建或打开一个命名的或无名的事件对象。
HANDLE CreateEvent(LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全属性,确定返回的句柄是否可被子进程继承。如果是NULL,此句柄不能被继承。BOOLbManualReset,// 复位方式,指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态 //复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。BOOLbInitialState,// 初始状态,指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态LPCTSTRlpName // 对象名称,指定事件的对象的名称,是一个以0结束的字符串指针。如果lpName为NULL,将创建一个无名的事件对象);
我们在此处创建的是一个无名的,不能被继承的,初始状态为无信号的,能够自动复原的事件。
BOOL ClearCommError(HANDLE hFile, //由CreateFile函数返回指向已打开串行口的句柄LPDWORD lpErrors, //指向定义了错误类型的32位变量LPCOMSTAT lpStat //指向一个返回设备状态的控制块COMSTAT);
WaitCommEvent(pDlg->m_hCom,&dwMask,&os)
BOOL WINAPI WaitCommEvent(__in HANDLEhFile, //指向通信设备的一个句柄,该句柄应该是由 CreateFile函数返回的。__out LPDWORDlpEvtMask, //一个指向DWORD的指针。如果发生错误,pEvtMask指向0,否则指向以下的某一事件__in LPOVERLAPPEDlpOverlapped //指向OVERLAPPED结构体的一个指针。如果hFile是用异步方式打开的(在CreateFile()函数中,第三个参数设置为FILE_F //LAG_OVERLAPPED),lpOverlapped不能指向一个空OVERLAPPED结构体,而是与Readfile()和WriteFile()中的OVE //RLAPPED参数为同一个参数。如果hFile是用异步方式打开的,而lpOverlapped指向一个空的OVERLAPPED结构体,那么函数/ //会错误地报告,等待的操作已经完成(而此时等待的操作可能还没有完成)。
//如果hFile是用异步方式打开的,而lpOverlapped指向一个非空的OVERLAPPED结构体,那么函数WaitCommEvent被默认为异 //步操作,马上返回。这时,OVERLAPPED结构体必须包含一个由CreateEvent()函数返回的手动重置事件对象的句柄hEven。);
GetOverlappedResult(pDlg->m_hCom,&os,&dwTrans,TRUE); //判断一个重叠操作的当前状态
GetOverlappedResult函数:BOOL GetOverlappedResult(HANDLE hFile, // 串口的句柄LPOVERLAPPED lpOverlapped, // 指向重叠操作开始时指定的OVERLAPPED结构LPDWORD lpNumberOfBytesTransferred, // 指向一个32位变量,该变量的值返回实际读写操作传输的字节数。BOOL bWait // 该参数用于指定函数是否一直等到重叠操作结束。 // 如果该参数为TRUE,函数直到操作结束才返回。 // 如果该参数为FALSE,函数直接返回,这时如果操作没有完成, // 通过调用GetLastError()函数会返回ERROR_IO_INCOMPLETE。);至此,关于串口监听线程的响应函数也完成了。当if(ComStat.cbInQue) //如果有读缓冲区中有数据{pDlg->ProcessCOMMNotification(EV_RXCHAR,0); //串口有数据到达时调用此函数}程序将会进入到主程序的ProcessCOMMNotification(EV_RXCHAR,0);函数进行数据的进一步处理。下一节中我们将会对ProcessCOMMNotification()函数进行详细的介绍。
- MFC之路 串口通信篇(之二)
- MFC之路 串口通信篇(之三)
- MFC之路 第二节 串口通信篇
- STM32之USART串口通信<二>
- 单片机之串口通信
- STM32之串口通信
- 魔方之串口通信
- 串口通信(二)
- UART串口通信浅谈之(二)--寄存器设置
- 51单片机之串口通信(一)
- WINCE程序设计之串口通信
- 串口通信之查询方式
- 串口通信之控件注册
- MCS-51之串口通信
- zigbee学习之串口通信
- BeagleBoneBlack学习之串口通信
- CC2530之UART串口通信
- STM32之USART(串口通信)
- 输入两个链表,找出它们的第一个公共结点。
- bootstrap日历控件
- acm Subsequence 3061
- Gradle从入门到实战
- c++学习系列之vector
- MFC之路 串口通信篇(之二)
- 学生信息系统优化总结
- Python学习(二)——控制流
- CSS垂直方向上文本居中
- Python如何运行程序
- RF,RobotFramwork使用Cookie获取验证码,登录账户
- React学习知识
- C++学习笔记-----string类的使用
- 好的用户体验来自好的设计策略