在剖析MFC之前,一定要先搞懂几个问题:
1.消息机制
windows系统的soul,无需多言,也没能力多言...
2.消息及消息队列
首先要清楚消息分两种,一种是要进入消息循环的的队列消息;一种是不进消息循环的非队列消息;在讨论消息循环时,一定是针对队列消息的;
对于windows系统,它维护着属于windows的唯一的消息队列。当有消息循环的线程执行时,它将分配该线程一个(或称为一组)属于该线程独有的消息队列。
对于一个线程,消息队列可分别SendMessage 队列、PostedMessage 队列、Visualized Input队列、ReplyMessage队列等等,其本质是按照不同的处理方式、来源,给消息分类排队,并定义了不同的处理优先级,使得线程能够对不同消息做出不同的处理。
关于消息队列的详细介绍,可见:
http://my.oschina.net/myspaceNUAA/blog/64682 或
http://blog.youkuaiyun.com/FreeWave/article/details/2056469或<windows 核心编程> 第四章。
3.消息从何而来
-来自系统设备的响应,如硬件设备驱动如键盘、鼠标、音频、串口等;
-来自应用程序本身,有一些是自动运行的,一些需要程序中实现,如改变窗口大小,重绘窗口、控件内容变化;另如定时器;
-来自应用程序中用户自定义的WM_USER+XX的消息;该类消息采用Send/PostMessage发送;
4.windows对象、句柄及MFC对象的关系
句柄用来标识windows对象。MFC使用C++对象,为了能与windows对象打交道,它封装了句柄,因此三者才能建立联系,形如【CWnd<->m_hWnd<->windows对象】
以下文中有详细解释:http://blog.youkuaiyun.com/banward/article/details/4418367 和http://blog.sina.com.cn/s/blog_493309600100cqhw.html
以下文中描述他们转换的方法:http://blog.163.com/fengxuedong_fxd/blog/static/719263062011920102930356/
他们的关系,以句柄映射Map的形式保存在创建线程的UI线程状态信息中。显然每个UI线程拥有自己掌控的独有的句柄列表,也即CWnd对象,这样可以限制多个线程同时访问同一个对象,从而引起错误。当一个线程使用某个MFC对象指针P时,ASSERT_VALID(P)将验证当前线程的当前模块是否有Windows句柄和P对应,即是否创建了P所指的Windows对象,验证失败导致ASSERT断言中断程序的执行。如果一个线程要使用其他线程的Windows对象,则必须传递Windows对象句柄,不能传递MFC对象指针。
由此应该知道,跨线程(这里指UI线程)的去更新一个CWnd对象时,不能直接用指针操作,因为发起者线程中(线程状态结构体中)并没有接收对象的句柄。有一种途径可以实现:
①在CWnd对象中自定义好消息,然后在另外线程中Send/PostMessage发送该消息给窗口;
句柄绑定对象的方法是先建立对象实例Cxx,Attach;解绑Detach;
5.MFC线程与消息循环的关系
线程分为Work Thread和UI Thread,前者不带消息循环,后者带消息循环。
UI线程对象启动执行后,windows为它维护一个消息队列,UI线程将执行自己的消息循环,即自动执行run,若不改写,即CWinThread::Run;
6.PeekMessage与GetMessage区别
都是获得消息。
Peek有偷窥之意,它抓一下消息队列中的消息,不管抓不抓到,都立即返回;
Get是强盗,一定要得到,进入阻塞,不获得消息绝不返回;
7.SendMeesage与Postmessage区别
都是把消息发送。
Send是直接发送。对于本线程的接收者,绕过消息循环,直接督促内核user32模块调用WinProc(最后即消息处理),并在发送期间阻塞,直至处理结束;对于跨线程的Send,则发送到接收者线程消息队列的Send队列中,此队列优先级别略高,可能最后仍然是由user32模块直接WinProc;
Post有寄送之意,轻巧地把消息放入接收者线程的消息队列,由消息循环去寻找去向,找到去向,再调用消息处理。