学习笔记之深入浅出MFC 第3讲 消息循环

来源:互联网 发布:独立游戏开发者 知乎 编辑:程序博客网 时间:2024/05/19 23:58

在上一节中讲的WinMain()函数中,最后一部分是消息处理循环,如下图所示:


其中,TranslateMessage是为了将键盘消息转化,DispatchMessage会将消息传给窗口函数去处理。

消息循环中的GetMessage是window 3.x非强制性多任务的关键。应用程序籍由此操作,提供了释放控制权的机会:如果消息队列上没有我的消息,我就把机会让给别人。通过程序之间彼此协调让步的方式,达到多任务功能。Windows 9x和NT具备强制性多任务功能,不再非靠GetMessage释放CPU控制权,但程序依然这么写,因为应用程序仍然需要消息推动。它还是需要抓消息的!

除此之外,很多朋友可能会疑惑DispatchMessage(&msg)是怎么实现消息分派的,这里我们大体的介绍一下:在消息发生时,操作系统会根据当前窗口激活状况为消息标明所属的窗口,而窗口所属的窗口类中又已经明白的标明了窗口函数(即wc.lpfnWndPro所指的函数),所以DispatchMessage就可以按照这个脉络传递消息给窗口函数了。

那么,我们就要顺着消息传递的脉络介绍一下窗口函数了。

消息经由DispatchMessage函数发送给窗口函数之后,窗口函数通过switch/case方式判断消息的种类来决定处置方式。这里不得不提一下窗口函数的调用方式,窗口函数是被Windows系统所调用的,我们不需要在任何地方调用这个函数,所以这是一种call back函数,意思就是“在你的程序中,被windows系统调用的函数”。这些函数虽然是由你设计的,但是永远不会也不应该被你调用,它们是为windows系统准备的。

窗口函数的形式如下所示:


在这个窗口函数中,是由switch/case结构来分别处理每一种消息的,对应每一种消息,都会有一种处理方式。但是,这里需要注意,所有的消息都必须被处理,所以除了我们明确需要处理的消息之外,其他消息必须用default处调用DefWindowProc进行默认的处理,这是一个Windows内部默认的消息处理函数。

窗口函数的wparam和lparam的意义随消息的不同而不同。wparam在16位环境中是16位,在32位环境中是32位。因此,参数的内容在不同的环境中就有了不同。

上面所介绍的消息传递及处理过程就是消息的原始循环原理,但是为了为了更加的模块化、一般化,在MFC中对消息映射(Message Map)做了一些结构化的处理。

首先,定义了一个MESSAGE_ENTRY结构和一个dim宏:


在这个结构体中,nMessagge是消息,第二个元素pfn是一个函数指针,在这里用此指针所指的函数处理nMessage消息。这正体现了面向对象观念中的把“数据”和“处理数据的方法”封装起来的一种具体实现。

然后,我们组织两个数组_messageEntries[]和_commandEntries[],把程序中欲处理的消息和消息处理例程的关联性建立起来:


注意,这是一个MESSAGE_ENTRY结构体数组,正好每一个数组元素对应一个消息及其处理函数。


这是命令项及其处理函数的结构体数组。

有了这两个结构体中消息和其处理函数的对应关系,接下来我们就可以这么设计窗口函数来处理消息了,如下:


在上面的代码中我们可以看到,在窗口函数中设计了两个for循环,分别处理两类消息。第一个for循环中,循环一遍消息,对于当前传入的消息message,在messageEntries结构体中找到此消息,并调用其结构体中对应的处理函数,也就是*pfn指针函数。同理处理命令消息。这样通过结构体和循环对应关系,取代了原来的switch/case结构消息处理机制。这样有个好处,WndProc和OnCommand永远不需要改变,每次有新的需要处理的消息的时候,只需要在_messageEntries[]和_commandEntries[]两个数组总加上新元素,并针对新消息撰写处理例程就可以了。

这种处理消息的机制正式MFC采取的消息处理方式,只不过MFC的Messae Map更加的精致。在MFC中定义一个新的消息的时候,只需要在Message Map中声明响应的处理函数,然后建立这个处理函数并在函数中做出处理就可以了。

0 0