当前位置:C++技术网 > 资讯 > Windows零基础入门:3.1 窗口过程函数的使用和背景知识详解

Windows零基础入门:3.1 窗口过程函数的使用和背景知识详解

更新时间:2015-09-24 11:37:14浏览次数:1+次

    通过前面一系列的课程,把窗口类风格总算讲完了,而且每一个风格都经过深度挖掘,我自己也是查了不少资料,思考过很久才学习到了不少东西。相信不管是你以前学没学过,都会有很多的收获。
    上节课说第二章差不多结束,并不是说不讲窗口类结构体变量的其他几个成员的赋值等内容了,而是其他几个成员,就只是一个赋值过程,需要讲的东西,则是其他一片天地,赋值很简单,而背后的各种技术背景,则不简单。我们都要用专门的章节讲。
    后面的几个成员,都与窗口类本身并不密切,不过因为是零基础入门的,所以,每一个成员使用中的各种细节,都会尽可能多的讲到。不仅让你会使用,而且知道为什么这么使用。
    第三章用来讲解WinMain函数中的代码的各种细节,而具体各个方面的深入知识,将以单独的章节详细介绍。本节课讲解窗口过程。
    窗口过程,也可以叫做窗口消息处理过程。实际上,它就是一个函数而已。然而,这个函数,并不是程序员写程序来调用的,而是给操作系统调用的,也就是回调函数。对于回调函数的理解和使用,请参考学习《简单的介绍回调函数原理和使用》和《函数调用与回调函数的设计原理的深入对比分析》。回调函数是什么,以及回调函数和函数调用的区别,我就不在此重复了。自己看文章哦。
    可是你知道吗?为什么窗口过程要做成回调函数呢?不知道你想过这个问题没有。作为Windows底层的结果和框架设计,必然是操作系统做好后就固定不变的,然而不同的程序需要不同的处理,怎么才能以不变应万变呢?是的,就是使用回调函数机制。

   
    在窗口类结构体变量中,提供一个成员,就是一个函数指针。函数指针指向的函数类型,是已经约定好的,必须按照这个格式来。这也是为什么MSDN都给定好回调函数的格式说明,为什么不按照这个格式写,就编译不通过的原因。如果你不按照指定的格式,底层就无法正确的完成功能。使用函数指针,就可以实现调用不同的函数,因为指针变量的值是可以随时修改的。前面我们不是修改过窗口类的窗口过程吗?还记得那个SetWindowLong函数吗?如果不记得,请反思自己的学习哦。我们的课程,学一课掌握一课,如果出现忘记前面的内容的情况,请尽快反思调整学习方式。学的每一节,涉及到代码的,一定要跟着动手练一遍或者多遍。如果你懒,想最快学会,想成为大神,做梦!对于函数指针的理解,请阅读《函数名、函数类型、函数地址和函数指针》。
    因为函数指针变量是可以它的值的,所以,你赋值不同的函数,就可以调用不同的函数。而这个函数指针变量,对于窗口类本身来说,是一个固定的成员,所以是不变的。只是它的值改变,对窗口类来说,并不需要改变正常的执行流程。只是函数指针去调用不同的函数罢了。
    那么,窗口过程函数作为回调函数,是如何起作用的呢?请先回顾一下窗口消息的整个过程,复习《Windows零基础入门:2.7 Win32窗口机制和消息机制整体流程》。
    消息产生之后,系统将消息发送给窗口的消息队列,然后窗口内部提取消息,处理好消息,就发送到窗口过程来处理。消息的信息,就是窗口过程函数中的参数传递进来的。所以,要处理所有消息,就要从窗口过程函数的参数中提取需要的信息。不同的消息,这些参数代表的意思都不一样,这些都要看MSDN的说明。也不用去记忆这些,用多了自然就知道了。
    因为Windows底层的窗口消息机制是固定的,所以,以回调函数实现窗口过程,并且窗口类的窗口过程成员是函数指针,这样,Windows系统不仅可以调用程序提供的函数,即回调函数,而且可以改变函数指针变量的值,调用不同的函数。修改窗口过程的知识和效果,请复习《Windows零基础入门:2.11 修改窗口类实现文字的切换》。
    微软规定了这个窗口过程的格式,那么我们就来仔细看看这些格式说明,而且各个参数代表什么意义。
    MSDN中,窗口过程函数的声明格式如下:

LRESULT CALLBACK WindowProc(
  _In_  HWND hwnd,
  _In_  UINT uMsg,
  _In_  WPARAM wParam,
  _In_  LPARAM lParam
);
     首先说一下,_In_对应的还有_Out_。很多人对这个确实很陌生。其实他们只是一个修饰符,让你更清楚这个参数是程序输入的,还是接受输出结果的。_In_自然就是调用函数时要主动传递进去的参数,而_Out_则是传递一个变量地址给函数,然后函数执行完后,会将结果复制到你传的变量里。如果你对这些没什么感觉的话,可以直接忽略掉他们。
    窗口过程函数的名字,你可以随意取,这个对于Window来说,没有什么感觉,因为程序代码编译之后,这些函数名只是一个代号,在二进制代码中,只是成为了函数代码的一个地址偏移数字而已。不过,你肯定要在窗口类结构体变量赋值给窗口过程时,要赋值你声明的这个函数咯。
    每一个函数都有返回值,窗口过程也是函数,当然也有返回值。不过怪异的是,又是一个“新类型”,LRESULT!你会接触越来越多的“新类型”,其实他们只是基本类型的别名哦。前面课程讲过一个钛合金眼镜哦,不知道你会用了没有。这里就不重复讲了。
    我们发现,LRESULT的声明如下:
typedef LONG_PTR LRESULT;
typedef long LONG_PTR;
     实际上,LRESULT就是一个long类型咯。至于返回值是消息的处理结果,并且不同的消息不一样。这里就不好说了。反正你知道,它返回的是一个long类型的值即可。
    而CALLBACK在讲解WinMain时已经讲过了,都是__stdcall的别名而已。参数中的hwnd就是消息中传递的窗口的句柄。系统要根据窗口句柄来投递消息,同时消息中自然含有窗口句柄的值,hwnd参数就是窗口句柄咯。
    为什么窗口处理过程中还要窗口句柄参数呢?因为窗口过程并不是一个窗口独立拥有的,它和窗口类一样,和很多窗口一起共同拥有,如果不用窗口句柄传递进去,窗口过程又怎么知道在处理哪个窗口的消息呢。
    第二参数uMsg则是消息的ID,代表一个消息,比如WM_PAINT重绘消息。在窗口过程中,通过switch对消息进行分类处理,不同的消息做不同的处理。这里就是一个识别消息对消息分类的效果。标准的消息都是以WM_开头的,WM_是Windows Message的缩写,就是窗口消息的意思。
    至于后面两个参数的类型,声明如下:
typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;
typedef unsigned int UINT_PTR;
typedef long LONG_PTR;
     那么他们实际上还是整型类型哦。不要太惊讶哦。实际上,需要的参数通过这两个参数传递,一般都是要经过类型强制转换的。具体使用,我们将在详细处理里面解释哦。
    不过,对于初学者来说,肯定分不清楚这两个参数的意思。实际上很简单的。这两个参数的值,都是根据第二个参数的消息ID不同而不同。第三个参数wParam与uMsg挨得近,自然也就是消息密切相关的信息了。而lParam则是除了密切相关的额外信息外,在第三个参数放不下,所以,在lParam中传递。不过,很多消息没有额外的信息,所以默认为0即可。
    声明好窗口过程函数后,然后给窗口类结构体变量的窗口过程成员赋值即可,直接填写窗口过程的函数名。声明窗口过程后,那么就要实现这个函数,就是在函数中写代码咯。至于在窗口过程写代码,这个在消息处理部分讲解。