MFC从线程向主界面发送消息(Message)的实现2020-08-04 21:42:54
实现消息的发送主要两种方式,SendMessage 与 PostMessage两种,这两种区别就是前者是阻塞的,后者是异步的,举个例子来说,Post像送报纸,心想一块钱的东西还要包邮,往门前一仍人就跑了,而Send方式是货到付款的,不收到钱他是不会回公司的。两个发送方式的函数原型及调用的方式是一样,传说返回值有点点不同,根据需要选择吧,一般来说,Send很快的,发送个消息又不传对象,都是秒回的,用的很多,Post感觉高大上一点吧,包含进程间传消息都不是事,这里不深入探讨,两种方式等下都发送下给你看,下面开始!
实现消息通信,主要分四步!
1)定义一个宏,
#define WM_TESTMESSAGE (WM_USER + 100)
这个宏随便写,只要不重复了就行,值大概 0x0400这样的,为什么我用WM_USER呢,因为这个宏是系统定义了的,而且用的很多,直接加个数就使用了方便,为什么加100呢,因为和你的想法一样,有不少组控件也用它,在后面+N,如果你加1,一不小心就和他们重复了,不过问题一般不大,暂且加100。这个宏放在哪里呢,网上太多人说要放在stdafx.h文件下,这是依赖系统项,谁会去改变不是自己的东西呢,我偏放在我自己的类顶上myDLG2020.cpp,因为我是本界面测试的,没有域限制,如果想多界面甚至多进程通信,只需要解决这个宏的作用域就好了,能调用到它就行
2)定义接收函数
LRESULT myDLG2020::OnReciveMessage(WPARAM wParam, LPARAM lParam) { CString cstr1 = (LPCTSTR)wParam; CString cstr2 = (LPCTSTR)lParam; TRACE(_T("Got it:wParam is: - %s\n"), cstr1); TRACE(_T("Got it:lParam is: - %s\n"), cstr2); return 1; }
看名字就知道,他是一个类内的方法,去.h中也加上声明吧 。
afx_msg LRESULT OnReciveMessage(WPARAM wParam, LPARAM lParam);
这里有个版本兼容的坑,如果你是老版本的vc6.0,这个函数返回值可能是void但是在VS2018 19新版,早就改为LRESULT 了,否则会报后面的参数转换失效。<MFC ON_MESSAGE 类型转换无效 >
3)定义发送函数
UINT myDLG2020::TestingMessage(LPVOID lParam) { myDLG2020* pWnd = (myDLG2020*)lParam; //线程内掉用线程外的内容的方式 //pWnd->KMeansSegment(); //调用外部函数 //pWnd->myTestParmter123; //调用外部变量 //CWnd* pWnd = CWnd::FindWindow(NULL, msg);//已经有句柄了 int i = 0; while (true) { if (pWnd->bThread2Stop) break; TRACE("Inner thread - %d\n",i); CString cstr1; cstr1.Format(_T("%d"), i); CString cstr2 = _T("Hello Test String!"); pWnd->SendMessage(WM_TESTMESSAGE, (WPARAM)(LPCTSTR)cstr1,(LPARAM)(LPCTSTR)cstr2); //pWnd->PostMessage(WM_TESTMESSAGE, (WPARAM)(LPCTSTR)cstr1, (LPARAM)(LPCTSTR)cstr2); //pWnd->PostMessage(WM_TESTMESSAGE, (WPARAM)(LPCTSTR)cstr1);//传一个值也是可以的,听说甚至可以传结构体或对象。。。 i++; Sleep(1000); } return 1; }
我这个函数是一个多线程内的函数,是由另一个button按纽事件下启用的
AfxBeginThread(TestingMessage, (LPVOID)this);
一般通信都是在多线程或多进程中,在一个做用域下也不用通信了。我讨厌文章繁琐,一般不相干的,我觉对不往一起掺杂,努力写成模板做到读者copy就能用,因为我以后也直接拷贝,也能给我省不少时间,这块只是单纯为了测试。相信你看到之后,不但会用,还对流程一清二楚,举一返三,就能熟练掌握了。这样说主要是怕我自己以后忘了,好丢人啊。。。
【讲解】本篇文章看到myDLG2020就是我主界面类,用的时候就改为你自己的,函数内部前三行,我上一篇文章讲过了,多线程获取主界面句柄,调用外部函数与变量更容易,这三行很不简单,打通了一个通道。其实用这个方法就可以降低message的依赖性了,但是message很强大,不但能传各种对象甚至能发送一个消息给“按纽”,对于程序逻辑精简至关重要!
接下来的是一个while循环,是测试占用时间的,再怎么费时的工作也不会阻塞主线程。bThread2Stop是一个外部的公共bool变量,是用来防止程序退出了,循环还在转,在结束的时候,将他设为true,这个逻辑是解决无限死循环的一个重要方法。
下一行是TRACE("Inner thread - %d\n",i);,TRACE是MFC调试利器,这个函数只在debug时输出到底部的“输出”窗口,很是直观,还带变量转换,很方便。
下面搞了两个变量,一个来源是一直在变的int型的参数,另一个是字符串,cstr1,cstr2。
下面调用发送消息函数,你看我写了三条,其实任意解开一条,都有效果,我只是想展现send与post与单、双参数的方法,需要说明的是,由于我给线程传送了pWnd这个句柄指针的,所以我直接用它发送了,但是函数原型中的第一个参数就是句柄,也就是说如果你要这样发送也是一回事SendMessage(hwnd,WM_TESTMESSAGE,...。这里的WM_TESTMESSAGE就是我们第一步定义的宏,大家都被穿在这一条线上了,举一返三,是不是可以定义多个宏,同时送三个不同的快递呢,由其在很拥挤的时候,这可能很有用。。。
Sleep因为里面没有耗时,输出会很快的,故做了个延迟。
4)绑定收发函数
BEGIN_MESSAGE_MAP(myDLG2020, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() 。。。。。。 ON_MESSAGE(WM_TESTMESSAGE, OnReciveMessage)//绑定 END_MESSAGE_MAP()
这几句话,不要看多,其实只要写一句话(就是注释//绑定那一行),其他内容方便定位位置用的,不要放错地方了,而是在cpp文件中 BEGIN_MESSAGE_MAP的尾部加上这么一行ON_MESSAGE,这些代码是新建MFC应用时默认就有的。如果你不是对话框模式或者没有这些代码,那可能需要再去头文件里定义一下。值得一提的是,对话框模式下默认还有下BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx),这是“关于对话框”那个绑定,不是这里不要搞错了,第一个参数应该是你的类名,对于初学者来说确实都是坑,比尔,你变了。。。
至此,一个完整精简的线程向主界面发送的消息就规划好了,我们运行一下,上面说了,我是在按纽事件中启动了线程,所以,点击了按纽后,每隔1秒就发送了一个消息到主界面,说明流程已经通了,说句实在话,这个收发过程与java中很类似,java中就是不用绑定而已。
最后上一张鼓舞图,按我说的来,能避开不少坑,而且如果够聪明,消息这块从此都不是事。想发啥就去查原型函数,看参数说明,想实现啥就改啥,你就比我厉害了。
话太多了,如果你是高手,肯定觉得繁琐,这还用说,多此一举,我也讨厌文章太长。。。下不为例。。。祝你成功!
原文链接(Hyperlink):https://snowcoal.com/article/75512.html
原创内容,尊重版权,转载需注明出处;商业及其他特殊用途转载需原作者同意。