文章目录
- 一、创建新的线程
-
- 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。