当前位置:C++技术网 > 精选软件 > Windows零基础入门:2.15 窗口类使用之窗口类结构体详解

Windows零基础入门:2.15 窗口类使用之窗口类结构体详解

更新时间:2015-09-08 21:48:11浏览次数:1+次

    本节课详细讲解了窗口类结构体的类型声明、窗口类结构体变量定义以及注册窗口类的相关背景知识,你可以全面的了解一下这个结构体,而不是只是当做一个类型来看待。为什么要用WNDCLASS,不只是设计出来了我们就用。有时候我们需要探究一下设计的初衷,才会发觉它的好处,也能更好的掌握。

    在《Windows零基础入门:2.14 (1)窗口类使用完整代码演示和详细注释》一节中,对于窗口类的使用代码细节,给出了详细的代码和相应的注释。能帮你看懂这个程序做了些什么。不过,请不要停留于此,这里是很概要性的,所以接下来的几节课将会详细讲述每一个部分。
    我们要先创建一个窗口类结构体变量,然后用结构体变量存储的窗口类信息去注册窗口类,然后就可以用注册好的窗口类来创建窗口。
    而我们要创建窗口类结构体变量,就要使用WNDCLASS结构体类型。实际上,我们还可以用WNDCLASSEX结构体类型来创建。WNDCLASSEX是对WNDCLASS的扩展。WNDCLASSEX比WNDCLASS结构体类型多了两个成员,一个是表示结构体大小的成员,一个是存储小图标的成员hIconSm,就是Small Icon的意思。而中间的一个hIcon图标就是比较大的图标。具体我们将在后面解释。你只要知道,带有EX字样的结构体或者Ex字样的函数都是对原函数的扩展,完成的功能都是一样的。
    下面是WNDCLASS结构体类型的定义,使用typedef重命名结构体类型,命名为WNDCLASS,加上*再命名一个指向结构体类型的指针类型。这是C/C++的基本语法,我想这点基础你应该还是有的。我们课程的零基础是说你对于Windows可以一无所知,但是对于编程语言要有基本的了解哦。

typedef struct tagWNDCLASS {
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
} WNDCLASS, *PWNDCLASS;

     正如上节课代码中所注释的,这些成员都有各自的意义。每一个结构体成员斗殴代表了窗口类的一个特性。我们为什么要这个结构体类型呢?其实也并不是非要不可的事情。前面课程说了,这个结构体类型,就像是生产窗口类变量的模板一样。结构体定义好各个成员,然后我们只需要填写各个成员的值,传给注册函数RegisterClass或者RegisterClassEx即可。实际上,这是在简化编程而已。结构体的出现,就可以将这些特性,在一个结构体中一次性的赋值好,否则,我们就需要将这么多的窗口类特性的参数一次性传入函数,那这样的函数十个窗口特性参数,再加上其他参数就是十几个参数,这样的函数你敢用吗?想想都难过吧。不知道会不会用多了就会睡不着觉,神经衰弱了呢?
    那么窗口类结构体就可以让你在传给注册函数前,一个个的赋值好,然后只需要传给函数一个窗口类结构体变量地址即可,注册函数在内部会从这个结构体变量中提取相应的参数。所以说,你不要觉得这个结构体很神奇,很神秘的样子,其实就是一个简化编程的而已。结构体还是和普通的结构体一样。
    而注册窗口类的函数RegisterClass或者RegisterClassEx,前者传入的是WNDCLASS,后者传入的是WNDCLASSEX类型的结构体变量,不要搞错了哦。扩展的结构体,用扩展的注册窗口类函数,就如同古代人和古代人结婚,现代人和现代人结婚一个道理。如果你搞错了,要么就是和鬼结婚了,要么就是时空逆转了。因为错配了,注册窗口类的函数就提取不到正确的参数,因为他们是根据变量存储成员的顺序来提取的。
    WNDCLASSEX结构体的定义如下:

typedef struct tagWNDCLASSEX {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
  HICON     hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;

     显然,这个和WNDCLASS的成员顺序不一样,也就会出错。因为他们的成员并不是前面一部分兼容的。如果把WNDCLASSEX传入了RegisterClass,那么RegisterClass就会把cbSize当做style处理,如果把WNDCLASS传给RegisterClassEx,那么RegisterClassEx会把style当做cbSize来计算结构体的大小。那岂不是错了嘛。所以,不要在这个细节翻跟头,不值哈。
    两者我们到底用哪个呢?现在都可以使用,只要配对正确就行。因为Ex版本多出了一个成员hIconSm,也就表示这个方式注册的窗口类,可以额外单独指定窗口的小图标,而不是统一使用hIcon指定的图标。
    那么我们创建窗口类结构体变量,就只需要写一个定义变量的语句如下即可:

WNDCLASS wndclass;

     然后,通过点号成员操作符来给每一个成员赋值,不需要使用的就赋值为0或者为NULL。这个赋值就和上节课所示的代码一样。如何赋值,应该赋哪些值,背景知识,将在后面的课程一一介绍。
    赋值好后,那么这个窗口类就定制完毕。那么就是调用RegisterClass来注册窗口类的时候了。RegisterClass函数只需要窗口类结构体变量的地址即可,使用取地址符&将变量取地址传入,如下:

RegisterClass(&wndclass);

     注册窗口类,只要是在Win32环境下,不会失败的。但是,你不能排除程序可能在DOS环境下运行,所以,这函数会返回状态值,如果注册失败,则返回为0,否则就表示注册成功。注册成功则返回一个窗口类的特定的ID值,唯一表示这个窗口类的。这个值可以在创建窗口CreateWindow(Ex)、获取类信息GetClassInfo(Ex)、查找窗口FindWindow(Ex)、注销窗口类UnregisterClass以及过滤客户区窗口方法IActiveIMMap::FilterClientWindows中使用。
    不管是用RegisterClass注册的窗口类还是用RegisterClassEx注册的窗口类,只要注册成功,都是一个窗口类,都是一类的,都有一个窗口类标志ID。这个都可以在上面这些函数中使用。注册窗口类完毕后,一般都是使用创建窗口函数。
    在使用时,不管使用哪个版本注册的窗口类,你都将获得一个窗口类。已经与注册窗口类的函数没有关系了。所以,这个窗口类并不要求一定要用CreateWindow还是CreateWindowEx,这里没有配套的说法。注册窗口类和使用窗口类是没有直接的关系。而创建窗口类变量需要直接传入注册窗口类的函数的,所以需要配套。不管你注册的窗口类是用哪个函数,都会注册成标准的样子,而创建窗口的函数也是从窗口类表去取信息,对于窗口类怎么创建不关心。
    而创建窗口的两个版本,扩展也只是让你多了定制窗口的样式罢了。完成的功能也是一样的。
    后面就是显示窗口,刷新客户区以显示客户区的内容,然后就是消息循环。这些是后面讲述的内容,此处略过。
    你似乎会怀疑,程序结束都没有发现注销窗口类的呢?当程序运行结束的时候,系统会自动将这些窗口类注销掉的。所以,不必你费心去处理这些事情。当然,你用完后,也可以手动注销,不过意义不大。除非你注销后,还想用这个窗口类名字注册一个不一样的窗口类,此时似乎有点必要。