[原创] MicroWindows学习笔记之消息管理
1、MSG
/*
* Message structure
*/
typedef struct tagMSG {
MWLIST link; /* microwin*/
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
所在文件:include\Winuser.h
2、MWLIST
其中MWLIST为一双向链表结构,其定义为
/* dbl linked list data structure*/
typedef struct _mwlist { /* LIST must be first decl in struct*/
struct _mwlist *next; /* next item*/
struct _mwlist *prev; /* previous item*/
} MWLIST, *PMWLIST;
所在文件:include\Mwtypes.h
3、MWLISTHEAD
/* dbl linked list head data structure*/
typedef struct _mwlisthead {
struct _mwlist *head; /* first item*/
struct _mwlist *tail; /* last item*/
} MWLISTHEAD, *PMWLISTHEAD;
全局变量
MWLISTHEAD mwMsgHead; /* application msg queue*/
它是一个MWLISTHEAD类型的数据,其定义见上,由它组成消息队列
消息队列
通过上面的数据结构,我们可以看见,MicroWindows的消息队列是通过链表的形式组织的。
整个消息通过mwMsgHead这个全局变量串联起来,按照先进先出的原则进出消息。其中,mwMsgHead.head是最老的消息,每次取消息都是从head开始取,而mwMsgHead.tail则是最新的消息,每次入消息都放在tail之后。
相关方法
所在文件:mwin\Winuser.c
1、SendMessage
1)方法原型:
LRESULT WINAPI
SendMessage(HWND hwnd, UINT Msg,WPARAM wParam,LPARAM lParam)
2)说明:
SendMessage是同步发送消息的方法。
思考:
当初在学Windows编程时,总觉得奇怪,为什么对于窗口、控件的操作,要通过WM_XXX消息的形式呢?通过API方法简单明了,非要用消息不是多此一举吗!前段时间与同事讨论MicroWindows时,突然发现这样做是另有目的。
大多数的图形系统,它的控件系统基本上都不支持多线程操作,即如果想操作控件的东西,那你只能在该控件对象所在的图形线程里做,在其他线程里操作都会有意想不到的后果,用户自己承担责任。这个主要是因为控件体系太过于庞大,做多线程保护会使整个工作量大大增加,而对于应用来说,单线程操作也基本上能满足要求。所以,应用在开发时,都需注意这一点。
而Windows通过消息来调用控件的方法,就可以有效的避免多线程操作的问题,因为通过消息把所有的操作进行序列化,放在图形线程里去执行,相当于做了COM里套间的功能。
如果想达到同步执行方法的话,可通过SendMessage方法。所以,对于SendMessage,我觉得MicroWindows的实现是有问题的,它只是简单的调用了窗口的回调函数(注册在窗口类上的WndProc),而没有考虑到多线程问题。如果在非图形线程里调用了SendMessage的话,那它有可能会在该线程里调用控件的方法,与图形线程冲突而导致问题。
2、PostMessage
1)方法原型
BOOL WINAPI
PostMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
2)说明
PostMessage是异步发送消息的方法。
将参数打包成MSG放入消息队列中。
其中WM_PAINT是特殊处理的,它并没有发消息,而只是在hwnd->gotPaintMsg置位,一来是因为WM_PAINT消息比较多,放在消息队列里不合适,二来通过置标志位达到WM_PAINT消息合并的效果,三来WM_PAINT优先级比较低,可以放在最后一个处理。
在PostMessage也通过开关做鼠标Move消息的合并工作。
3、GetMessage
1)方法原型
BOOL WINAPI
GetMessage(LPMSG lpMsg,HWND hwnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
2)说明
从消息队列中取消息,并将该消息从消息队列中摘除。一般,应用的消息循环都是通过这个方法来取得消息的。
里面的实现是通过PeekMessage来做的。返回值决定要不要退出消息循环。
4、PeekMessage
1)方法原型
BOOL WINAPI
PeekMessage(LPMSG lpMsg, HWND hwnd, UINT uMsgFilterMin, UINT uMsgFilterMax,
UINT wRemoveMsg)
2)说明
用于从消息队列中取消息,要不要将该消息从消息队列中摘除,取决于最后一个参数wRemoveMsg,它的取值范围为:

其实这个函数才是整个MicroWindows消息的源动力,所有的取消息都从这个入口开始,而且底层的硬件消息也从这里读取。
在这里,做了一个简单的消息优先级的处理:
1] 如果消息队列里有消息(mwMsgHead.head非空),则先处理消息队列里的消息;
2] 当消息队列为空时,看所有窗口有没有paint标志,如果有,则发paint消息;
3] 如果窗口没有paint标志,那么调用MwSelect方法取底层消息;
4] 再没有消息,那直接返回FALSE。
思考:
在PeekMessage取不到任何消息时,它会返回FALSE,而GetMessage是这么实现的

所以,会不会在没有消息时一直死循环呢?
5、TranslateMessage
1)方法原型
BOOL WINAPI
TranslateMessage(CONST MSG *lpMsg)
2)说明
这个方法是将WM_KEYDOWN转换为WM_CHAR。
6、DispatchMessage
1)方法原型
LONG WINAPI
DispatchMessage(CONST MSG *lpMsg)
2)说明
这个方法是将消息传到窗口的回调函数(窗口类中的WndProc)。
浙公网安备 33010602011771号