【求助】关于新建线程中,用MSComm控件接收数据的问题

Squall001 2014-05-07 03:47:04
一个关于新建线程中,用MSComm控件接收数据的问题,现在情况是这样。
我在主窗口拖了一个MSComm控件到窗口里面去, 用类向导关联这个控件,并且设变量名为m_ComPort。
然后把这个m_ComPort初始化好,并且把事件响应这些函数都处理都弄好。

以下是我主窗口程序事件的定义和接收数据函数
我先在头文件里面定义一个数组 用来装数据,和之前说的MSComm控件变量


CMSComm m_ComPort;
BYTE rxdata[1024];


然后在CPP文件里面做了如下定义


void CMyPortDlg:Init()
{
这里里面做m_ComPort;初始化的操作主要为:
m_ComPort.SetCommPort(1); //选择COM1
m_ComPort.SetInBufferSize(1024); //接收缓冲区
m_ComPort.SetOutBufferSize(1024);//发送缓冲区
m_ComPort.SetInputLen(0);//设置当前接收区数据长度为0,表示全部读取
m_ComPort.SetInputMode(1);//以二进制方式读写数据
m_ComPort.SetRThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnComm事件
m_ComPort.SetSettings("9600,n,8,1");//波特率9600无检验位,8个数据位,1个停止位

….


}

void CMyPortDlg:Send(CString strData)
{
这个里面把参数strData传进来的字符 转换成16进制数据,通过串口发出去
}
BEGIN_EVENTSINK_MAP(CSportDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CSportDlg)
ON_EVENT(CMyPortDlg, IDC_MSCOMM1, 1 /* OnComm */, OnCommMscomm1, VTS_NONE)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CMyPortDlg::OnCommMscomm1()
{
// TODO: Add your control notification handler code here
VARIANT variant1;
COleSafeArray safearray;
LONG len,k;
if(m_ComPort.GetCommEvent()==2)
{
variant1 = m_ComPort.GetInput();
safearray = variant1;
len = safearray.GetOneDimSize();
for(k=0;k<len;k++)
{
safearray.GetElement(&k,rxdata+k);
}
然后再简单的分析数据,几行代码;
}
}


如果用以上代码 在主窗口线程里面去发送和接收都没有问题。我发送一个数据给下位机,下位机马上就反馈数据回来给我程序了。

但是我因为程序需要,需要在新建线程里面去发送数据和接收数据,结果就带来了以下问题,把我困扰惨了。。。

我新建线程AfxBeginThread(MyThread1,this);然后把主窗口线程的this指针传给新建线程,为了就是调用主窗口的CMyPortDlg:Send()这个发送数据函数;


UINT CProduceDlg::MyThread1(void *param)
{
CMyPortDlg * Dlg =(CMyPortDlg *)param;


}

下面我要在新线程里面给下位机发命令,让下位机启动一段时间,让它搜集数据,过5秒后发命令让其关闭,然后我再发命令给下位机,让它返回一个通过刚才5秒钟收集数据,然后经过处理后的数据。

然后我在新建线程MyThread1里面这样做


UINT CProduceDlg::MyThread1(void *param)
{
CMyPortDlg * Dlg =(CMyPortDlg *)param;
Dlg->Send("FFAAAAFF"); //发送启动命令
Sleep(5000); //这里延迟5秒 ,并且让主窗口线程响应ON_EVENT事件
Dlg->Send("FF0000FF"); //发送关闭命令
Sleep(500); //这里延迟一下,也让主窗口响应ON_EVENT
Dlg->Send("FFBBBBFF"); //发送查询命令
Sleep(1000); //延迟1秒,留出足够的时间让下位机返回数据
然后操作rxdata[]里面的数据
}


我记得很早听人说 Sleep这个函数可以让出CPU执行权,在Sleep的时间里面,可以让其他线程执行程序,所以我故意在发送启动命令后延迟5秒,我以为这5秒可以让主窗口线程响应足够多的ON_EVENT事件,结果调试跟踪发现,在延迟5秒期间,主程序1次都没有响应ON_EVENT事件,而是在我发送Dlg->Send("FF0000FF ");这个关闭命令过后,主程序里面的ON_EVENT事件才来,而且来的不是m_ComPort.GetCommEvent()==2,GetCommEvent()返回的是1008,也就是“缓冲区溢出”,(因为我给下位机发送启动命令后,下位机会每3ms就发送一个数据给我电脑),而且是连续返回几个这个溢出事件,等着几个溢出事件响应完了,程序执行“Dlg->Send("FFBBBBFF");//发送查询命令”过后,新线程里面再执行Sleep(1000);,在这期间主窗口也不会响应ON_EVENT事件,所以我写在OnComm里面的那些数据处理操作根本就不执行,以至于后面我新线程里面最后 “操作rxdata[]里面的数据”,根本就是无效操作。

于是我把代码改为下面一种

UINT CProduceDlg::MyThread1(void *param)
{
CMyPortDlg * Dlg =(CMyPortDlg *)param;
Dlg->Send("FFAAAAFF"); //发送启动命令
pMyThread->SuspendThread() //把线程挂起 然后在 OnComm里面判断收到足够数据就恢复

..
}

结果 把线程一挂起,线程就永远挂起了,主窗口线程也不会响应任何一个ON_EVENT事件,也就根本就无法进入到OnComm函数,也就无法恢复。

我看MSDN里面说Sleep(0);可以让出时间片,让后我加进MyThread1里面各个地方,各种不同加法,结果还是不行,主线程都不得响应ON_EVENT,或者像我注释里面 理想的那样 去响应ON_EVENT事件,从而顺利的进入OnComm里面去操作数据。。。

我能确定在线程里面Dlg->Send("FFAAAAFF");这个命令一定起作用了的,下位机也不断向我电脑发数据,我下位机上有显示的。关键问题就是在响应ON_EVENT事件这里出问题。

客户那边催得相当厉害,小弟半夜三更整理问题求助,望好心人指点一二,不甚感激。

...全文
219 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jarrylogin 2014-05-08
  • 打赏
  • 举报
回复
en, 能满足用户需求就行,用户就是看看功能实现,才不管什么方式实现的。
洗洗睡去 2014-05-08
  • 打赏
  • 举报
回复
你的方法不算差的 本来就有应答式交互的方式 事件的感觉高级点 哈哈
洗洗睡去 2014-05-07
  • 打赏
  • 举报
回复
用第三方类比较好 CnCom 或者CserialPort 从这2个类派生出来自己的类 把发送和接受函数封装在里面,线程里直接操作,不用sleep 需要dlg操作的时候就发消息,我就这样做的 效果很好的
Jarrylogin 2014-05-07
  • 打赏
  • 举报
回复
给个建议: 你可能在 设计思路 上出了问题。 据我所知: Comm控件是需要附在对话框上面的,而且是OnEvent事件触发。这是在主线程中处理的 如果你需要用线程进行收和发处理的话,还是用 API或者CSerialPort类比较好, 毕竟效率高很多。
zgl7903 2014-05-07
  • 打赏
  • 举报
回复
用API吧 http://msdn.microsoft.com/en-us/library/ms810467.aspx
Squall001 2014-05-07
  • 打赏
  • 举报
回复
引用 2 楼 schlafenhamster 的回复:
在OnTimer 中转移状态: switch(Step) [ case 1: Dlg->Send("FFAAAAFF"); //发送启动命令 Step=2; break; case 2: Dlg->Send("FF0000FF"); //发送关闭命令 Step=3; break; case 3: Dlg->Send("FFBBBBFF"); //发送查询命令 Step=1; break; } }
引用 5 楼 sunnyloves 的回复:
用第三方类比较好 CnCom 或者CserialPort 从这2个类派生出来自己的类 把发送和接受函数封装在里面,线程里直接操作,不用sleep 需要dlg操作的时候就发消息,我就这样做的 效果很好的
引用 4 楼 Jarrylogin 的回复:
给个建议: 你可能在 设计思路 上出了问题。 据我所知: Comm控件是需要附在对话框上面的,而且是OnEvent事件触发。这是在主线程中处理的 如果你需要用线程进行收和发处理的话,还是用 API或者CSerialPort类比较好, 毕竟效率高很多。
当初就是不懂串口 才去研究的用控件,简单的方法。。。等以后有空了再来看看用API,话说用API才是王道。。。 不过我昨天发帖过后,百度的时候,运气来了,看到有一个人的帖子,好像是一个留言,看到人家也是在新线程里面接收数据,也遇到问题。但是它是在新线程里面调用m_ComPort.GetInput();强制收,也就是不等那个ON_EVEN事件。因为实际情况,一般用232 或者485,给串口发数据后都有返回值,所以,我就在新线程里面 发1个数据,然后sleep(100);然后调用主窗口线程的m_ComPort.GetInput();去强制收一次,收不收得到再说。用此方法,在我Sleep(5000);的时候,根据我的情况,真的等这么久,肯定会缓冲区溢出,所以我就每隔500ms强制收一次,这样收8次 也就是5秒钟啦, 这个就是我现在想到的临时解决方法,现在用起来还可以,哈哈,用户反映不错,可惜啊,只是他看不到我背后是用怎么个糟糕的方式实现的。。。。。。。。。。。
Squall001 2014-05-07
  • 打赏
  • 举报
回复
引用 2 楼 schlafenhamster 的回复:
在OnTimer 中转移状态: switch(Step) [ case 1: Dlg->Send("FFAAAAFF"); //发送启动命令 Step=2; break; case 2: Dlg->Send("FF0000FF"); //发送关闭命令 Step=3; break; case 3: Dlg->Send("FFBBBBFF"); //发送查询命令 Step=1; break; } }
发送没有问题,我测试 都发送出去了的,没有问题。只是接收的时候不能进入那个OnCom。
schlafenhamster 2014-05-07
  • 打赏
  • 举报
回复
在OnTimer 中转移状态: switch(Step) [ case 1: Dlg->Send("FFAAAAFF"); //发送启动命令 Step=2; break; case 2: Dlg->Send("FF0000FF"); //发送关闭命令 Step=3; break; case 3: Dlg->Send("FFBBBBFF"); //发送查询命令 Step=1; break; } }
schlafenhamster 2014-05-07
  • 打赏
  • 举报
回复
Sleep(5000); 改SetTimer(1,5000,0);

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧