在上一篇blog中,我大致介绍了一下构建一个简单窗口的必备要素及其逻辑。今天就来聊聊win32的消息机制。
首先消息是什么?
windows系统的消息有且只有6部分组成,分别是窗口句柄,消息ID(作用),消息产生时鼠标的位置,消息产生的时间,以及跟消息内容相关的参数。
typedef struct tagMSG {
HWND hwnd;//窗口句柄
UINT message;//消息内容
WPARAM wParam;//消息类别(附带信息
LPARAM lParam;//消息类别
DWORD time;//消息产生时间
POINT pt;//鼠标所在位置
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
然后,消息是从哪里产生的?windows中从根源上产生消息的是两个函数
BOOL SendMessage(HWND hWnd, //消息发送目的窗口
UINT msgID, //消息ID
WPARAM wParam, //消息参数
LPARAM iParam);
BOOL PostMessage(HWND hWnd, //消息发送目的窗口
UINT msgID, //消息ID
WPARAM wParam, //消息参数
LPARAM iParam);
//send比较专一,会痴痴地等待消息回复(消息的处理结果)。而post则是发出消息就不管不顾了。
其他函数正是通过调用他们,来实现生产消息。
接下来,消息都会到哪儿去?
根据消息产生的函数,有两种途径。不过我们要先来聊聊消息的“居所”,其名队列(FIFO),系统消息队列存储所有消息,而一般的程序队列则存储该线程的消息。这两者之间的关系好比总部与分部之间的关系,总部先收容所有的消息,每隔一段时间分发给分部(记住,msg中有hWnd,可以以此来确定目的地)。
至此,第一种途径就已经明了了。消息产生后,先到系统队列,再到程序队列,然后被该程序的getmessage抓到。这类消息是由postmessage发送的。以WM_QIUT为代表
而第二种途径则是sendmessage直达消息处理函数函数。以WM_CREATE为代表
再接下来是获取消息。这是getmessage与peekmessage的主场
BOOL
GetMessage(
LPMSG lpMsg, //information
HWND hWnd, //handle to window
UINT wMsgFilterMin,// first message
UINT wMsgFilterMax,// last message
UINT wRemoveMsg//PM_REMOVE\PM_NOREMOVE
);//移除消息,阻塞函数,若无消息,会等候
BOOL
PeekMessage(
LPMSG lpMsg, //information
HWND hWnd, //handle to window
UINT wMsgFilterMin,// first message
UINT wMsgFilterMax,// last message
UINT wRemoveMsg//PM_REMOVE\PM_NOREMOVE
);//以查看的方式获取消息,可以不移除,非阻塞函数,当系统无消息,返回FALSE,继续执行
我们将GetMessage比作战士,而PeekMessage则是侦查兵。第一个参数是当前的消息,或者说敌情,而hWnd与之后的两个参数限定了他们处理的范围。PeekMessage的最后一个参数决定了是否要给这个函数处理消息的权限(仅仅是移除而已)。
因此我们一般将peekmessage的最后一个参数设置为PM_NOREMOVE,让getmessage去抓消息。
现在具体讲讲GetMessage的工作:一般认为,它没有消息就待机,实际并非如此。
首先去检查程序队列里有没有消息,如果有,则处理,没有,就去向系统队列要,如果有,此时系统队列会直接发送给程序队列。如果没有,GetMessage会检查需不需要重新绘制窗口,如果需要,发送重新绘制窗口的消息,如果不需要,去检查定时器时间,如果有,就发送相关消息,如果没有,就处理系统资源。如果不需要处理,才开始等待。而PeekMessage则是在这个步骤里选择返回。
最后就是消息处理
//消息循环(获取、翻译、派发)
MSG nMsg = { 0 };
while (GetMessage(&nMsg, nullptr, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息给窗口处理函数来处理
}
翻译消息翻译的是键盘消息。By the way of analogy,the same key "a" can present as a or A,which depends on whether the CapsLock is opened.所以需要简单翻译一下。
接下来是DispatchMessage,本质是调用自己编写的消息处理函数。
至此,消息循环的大体内容就结束了