天天看点

c++学习 | MFC —— 串口通信(二)创建线程一、创建新的线程二、创建新线程的响应函数

文章目录

  • 一、创建新的线程
    • 1.源代码
    • 2.AfxBeginThread 函数 —— 创建线程
  • 二、创建新线程的响应函数
    • 1.源函数
    • 2.API函数详解
      • (1)OVERLAPPED结构体 —— 记录串口的操作信息
      • (2)COMSTAT结构体 —— 记录串口的信息
      • (3)CreateEvent()函数
      • (4)ClearCommError()函数——清除状态
      • (5)WaitCommEvent()函数——等待一个事件发生
      • (6)GetOverlappedResult()函数——检索某重叠操作的结果

  在前面一个章节的文章中,我们对串口进行了打开和参数的设置,接下来我们需要创建一个新的线程完成对串口的数据监听功能。

  创建新的线程,一般分为两个部分,一个是创建一个线程,另一个就是创建线程的响应函数

一、创建新的线程

1.源代码

在打开串口函数中 创建工作献线程
//打开串口
BOOL CSprDlg::OpenComm(int Num)
{
//创建工作者线程
	if (SetCommParameter()) // 如果串口设置成功的话,接着创建新的线程
	{
		m_pThread = AfxBeginThread(ComProce, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
		//创建新线程函数,返回线程的指针


		if (m_pThread == NULL)//如果为NULL,表示创建失败
		{
			CloseHandle(m_hCom);			//关闭前面创建的串口句柄
			AfxMessageBox("线程创建失败!");
			m_bConnected = 0;					//将连接标志置0
			return FALSE;						//返回
		}
		else
		{
			m_pThread->ResumeThread();			//如果创建新线程成功了,调用线程恢复函数恢复线程
		}
	}
	else            //如果串口设置没成功,直接返回
	{
		CloseHandle(m_hCom);
		AfxMessageBox("参数设置失败!");
		m_bConnected = 0;
		return FALSE;
}
           

2.AfxBeginThread 函数 —— 创建线程

CWinThread* AfxBeginThread(
					AFX_THREADPROC 			pfnThreadProc,		//线程函数地址
				   	LPVOID 					pParam,				//线程参数
				   	int 					nPriority = THREAD_PRIORITY_NORMAL,	//线程优先级
				   	UINT 					nStackSize = 0,		//线程堆栈大小,默认为1M
				  	DWORD 					dwCreateFlags = 0,	//创建标识,CREATE_SUSPENDED悬挂状态创建
				   	LPSECURITY_ATTRIBUTES 	lpSecurityAttrs = NULL //安全属性
);
           

pfnThreadProc : 线程的入口函数,声明一定要如下:

UINT MyThreadFunction(LPVOID pParam)

,不能设置为NULL;

pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.

nPriority : 线程的优先级,一般设置为 0 。让它和主线程具有共同的优先级.

nStackSize : 指定新创建的线程的栈的大小。如果为 0,新创建的线程具有和主线程一样的大小的栈

dwCreateFlags : 指定创建线程以后,线程有怎么样的标志。可以指定两个值:

     ● CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用:ResumeThread。

     ● 0 : 创建线程后就开始运行。

lpSecurityAttrs : 指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL,那么新创建的线程就具有和主线程一样的安全性。

二、创建新线程的响应函数

1.源函数

  串口监听线程的响应函数,这个定义只能放在.cpp文件开头,放在头文件中出错

//串口线程响应函数
//串口监听线程的响应函数
UINT ComProce(LPVOID pParam)
{
	OVERLAPPED os;			//重叠操作I/O结构体
	DWORD dwMask, dwTrans;	//无符号长整型,标志位
	COMSTAT ComStat;		//包含串口信息的结构体
	DWORD dwErrorFlags;		//错误标志位

	CSprDlg* pDlg = (CSprDlg*)pParam;		//参数传入为this,即对话框类指针

	memset(&os, 0, sizeof(OVERLAPPED));		//清空os结构体
	os.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);		//创建一个事件对象,将其赋值给os结构体

	if (os.hEvent == NULL)		//创建事件失败
	{
		AfxMessageBox("不能建立事件对象!");
		return (UINT)-1;
	}

	while (pDlg->m_bConnected)		//如果串口通信已经连接
	{
		ClearCommError(pDlg->m_hCom, &dwErrorFlags, &ComStat);	//清除硬件的通讯错误以及获取通讯设备的当前状态

		if (ComStat.cbInQue)	//如果有数据到达
		{
			pDlg->ProcessCOMMNotification(EV_RXCHAR, 0);//串口有数据到达时调用此函数
		    ——————————————————————————————————————————————该函数下节讲
		}


		dwMask = 0;//没有数据时置0
		if (!WaitCommEvent(pDlg->m_hCom, &dwMask, &os))	//为一个特指的通信设备等待一个事件发生,成功返回非0,失败返回0
		{
			if (GetLastError() == ERROR_IO_PENDING)	//如果错误信息为ERROR_IO_PENDING,表示数据正在传输中
			{
				GetOverlappedResult(pDlg->m_hCom, &os, &dwTrans, TRUE);//判断一个重叠操作的当前状态
			}
			else     //如果错误信息为其他,说明通信出现问题,结束串口通信线程
			{
				CloseHandle(os.hEvent);
				return(UINT)-1;
			}
		}
	}
	CloseHandle(os.hEvent); //线程结束,关闭事件

	return 0;
}
           

2.API函数详解

(1)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的方式构造或打开文件,在我们前面的代码中正是应用了这种方式。

(2)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;
           

(3)CreateEvent()函数

  

CreateEvent

是一个Windows API函数。它用来创建或打开一个命名的或无名的事件对象。

  我们在此处创建的是一个无名的,不能被继承的,初始状态为无信号的,能够自动复原的事件。

HANDLE  CreateEvent(
		  LPSECURITY_ATTRIBUTES  	lpEventAttributes,// 安全属性 
		  BOOL  					bManualReset,// 复位方式
		  BOOL  					bInitialState,// 初始状态
		  LPCTSTR  				lpName // 对象名称 
); 
           
  • lpEventAttributes: 句柄可否被继承,NULL表示不能继承
  • bManualReset: True表示手动复位,必须通过ResetEvent手动将事件恢复到无信号状态;False表示自动复位,当一个线程被释放以后,系统将自动将事件状态复位为无信号状态;对于自动复位的Event对象,仅释放第一个等待到该事件的线程,对其它线程无效;手动复位的Event对象对所有线程有效
  • bInitialState: True,初始状态有信号;False,初始状态无信号
  • lpName: 事件对象名称,NULL表示无名事件对象

(4)ClearCommError()函数——清除状态

  Windows系统利用此函数清除硬件的通讯错误以及获取通讯设备的当前状态

BOOL ClearCommError(
			HANDLE 		hFile,   //由CreateFile函数返回指向已打开串行口的句柄
			LPDWORD 	lpErrors, //指向定义了错误类型的32位变量
			LPCOMSTAT 	lpStat  //指向一个返回设备状态的控制块COMSTAT
);
           

(5)WaitCommEvent()函数——等待一个事件发生

  使用SetCommMask函数设置所要检测的事件后,当此事件发生时,就可以用WaitCommEvent得知该事件是否已发生,若此函数返回True,表示设置的信息已发生

BOOL WaitComrnEvent(
           HANDLE			hFi1e,  //通信设备的句柄
           LPWORD 			lpEvtMask,//发生的事件变量的地址
           LPOVERLAPFED 	lpoverlapped //Overlapped结构的地址
)
           

WaitCommEvent函数参数说明如下:

  • hFiie: 串行端口的Handle值,此值即为使用CreateFile函数后所返回的值。
  • IpEvtMask: 指向所检测到的信息的参数地址,32位长度,信息常数如SetCommMask函数的第二个参数,若发生错误,则此值返回0。
  • IpOverlapped: 使用Overlapped方式打开文件时应给定的结构,在串行通信中若不采用后台工作时,则可不使用;但是在不使用Overlapped的情形下,若己设置了信息屏蔽,则会使得程序停在比函数上,一直到有事件发生,并被检到为止,才会离开等待的状态,因此使用时需注意,最好还是使用Overlapped结构在等待息的程序。

(6)GetOverlappedResult()函数——检索某重叠操作的结果

BOOL GetOverlappedResult( HANDLE hFile,
			LPOVERLAPPED 	lpOverlapped,
			LPDWORD 		lpNumberOfBytesTransferred,
			BOOL 			bWait
 );
           

GetOverlappedResult函数参数说明如下:

  • hFile: 文件,通信设备或管道的句柄。
  • lpOverlapped: LPOVERLAPPED 结构体的指针,用于说明重叠操作是否开始,该参数和readfile函数或writefile函数中的LPOVERLAPPED 结构体的even参数相匹配;
  • lpNumberOfBytesTransferred: 一个指向字节数的指针,该字节数是读操作或写操作的实际传输字节数。
  • bWait: 当LPOVERLAPPED 结构体的内部参数为STATUS_PENDING,且该参数为TRUE,那么只有当操作完成才会返回。当该参数为FALSE,且操作正在等待,则返回FALSE,用GetLastError 函数会返回ERROR_IO_INCOMPLETE。

继续阅读