当前位置:C++技术网 > 精选软件 > Windows零基础入门:2.10 三种窗口类的深入全面详细分析

Windows零基础入门:2.10 三种窗口类的深入全面详细分析

更新时间:2015-08-20 21:22:13浏览次数:1+次

    由前面课程我们知道了,窗口类是有三种类型的。这节课就是详细介绍这三种窗口类类型的。扩展到皮肤库的原理分析和系统主题的原理分析。
三种类型分别如下:
1.系统窗口类
2.应用全局窗口类
3.应用局部窗口类
----------------------------------------------
1.系统窗口类
    下表是可供所有进程使用的公用系统窗口类:
Button        按钮控件类
ComboBox  组合框控件类
Edit           编辑框控件类
ListBox      列表框控件类
MDIClient   MDI窗口类 
ScrollBar   滚动条控件类
Static       静态控件类

    下面是系统内部使用系统窗口类:
ComboLBox  包含在组合框中的列表框控件类 
DDEMLEvent 动态数据交换管理库事件类
Message    消息窗口类
#32768     菜单类
#32769     桌面窗口类
#32770     对话框类
#32771     任务切换窗口类
#32772     图标标题类
 
    系统窗口类是由系统创建和维护的,在任何一个线程请求了公用系统窗口类时,也就是用系统窗口类名来创建窗口时,这个时候,系统就创建了这个窗口类。然后再复制一份到请求的进程内存中。其他进程的线程再用这个窗口类创建,则直接复制给他就行了。
    以上列出的七种可用的系统窗口类就是我们常说的标准控件或者叫做公共控件。公共一个概念就是相对于系统内部使用的系统窗口类而言的。第二个部分列出来了系统内部使用的窗口类,我们不必去研究,这里只是作为知识的完整性,列出来。让大家有个直观的认识。你可以看到下面几个,甚至都没有名字,直接用数字编号做名字。
    我们使用公共控件时就是直接用这个窗口类名来创建控件的过程。具体的控件介绍和使用,在后续课程会讲。这里就是让你对控件的来源有个清晰的了解。
    这几个窗口类的名字是不可以乱写的,固定的,必须这么写。而且系统窗口类设置的窗口过程也是预先就设定好的。你要改变,就要子类化来修改。这个在上节课做了详细的介绍。
    相对于系统窗口类来说,应用程序的窗口类类名和窗口过程等都是我们自己的确定,我们得到了这些自由,也就要付出完全要自己从头到尾来管这个窗口类的代价。当然,我在学习应用程序窗口类的过程也是促进对于窗口类的了解。因为过程都是一样的,只是系统窗口类帮我们做了很多事情,因此我们也少接触了很多东西。应用程序窗口类,就展现了所有的细节。

2.应用全局窗口类和应用局部窗口类
    全局窗口类,可以由exe的线程来创建,也可以由dll模块来创建。这个对于应用程序局部窗口类来说也是一样的。区别就在于创建这个窗口类时给窗口类指定的标志不一样罢了。这个标志就是CS_GLOBALCLASS。CS是Class Style(类风格)的缩写。GLOBALCLASS就是Global Class的大写,表示全局类的意思。合起来就表示全局类的类风格。只要将这个窗口类的风格标志加入到自己创建的窗口类中,就可以了。代码演示如下:
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
...
RegisterClass(&wndclass);
     WNDCLASS就是创建你自己的窗口类的一个结构体,是一种数据类型,也就是你的窗口类的模板,只要创建这个结构体的变量,就产生了你的自定义的窗口类了。所以我们的窗口类是wndclass,而不是WNDCLASS。wndclass是具体的实实在在的窗口类,WNDCLASS只是一个创建窗口类的数据类型而已。这个结合前面的文章,你就会认识的更加清楚了。
    在你的窗口类的类风格中,指定了CS_GLOBALCLASS标志,你创建的这个类就是应用程序的全局类了。不管你的代码是在一个exe里,还是在dll里,都无所谓。你只需要关注这个标志即可。
    类风格就是一个整型变量,所以,这里是通过位或操作,将这个标志写入类风格的。这个做法在编程中非常常见,即用一个int变量存储多种标志,用int变量中的一个位存放一个状态,而要对这个状态修改,就要用位操作来完成了。当然,直接改变数字也是可以达到改变位的状态的。比如赋值为1,就是最低一位是1,其他全位零。如果赋值为8,那么就是从低到高的第四位为1,其他都为0。这种做法也是可行的,只不过需要你自己去处理,比较麻烦。Windows提供了各种宏,这些宏就是一些数字,内部然后位或组合起来。比如,CS_GLOBALCLASS宏就是0x4000。对于数字来讲,你肯定不容易辨别了,使用宏就一眼知道它代表什么了。所以,建议适用使用这些宏,小技巧大作用。我们在使用时,要加入一个标志,就是位或。一个宏就表示其中一个位,把它加入就是将对应的这个位设置为1,去掉它,就是将这个位设置为0.
    那么上面就说了应用程序窗口类的使用和一些信息。我们的学习当然不是就此结束,还有很多要介绍的。
    应用程序全局窗口类,作为一个进程公有的,所以,一个人创建好,其他人都可以用了。这是公共设施,只要生活在这里的人都可以享用。就好比你是一个小区的住户,小区的基础建设,比如练身的器材,比如石桌等,你都可以用。
    这里用全局窗口类就是拿来创建窗口。如果我写好一个很漂亮的一套控件,生成一个dll,然后将这个dll给你用,你放在你的工程里,然后在程序中,调用dll的函数去注册dll里的所有应用程序窗口类,这样,你的程序就可以使用我做好的控件了。我这个dll美其名曰皮肤库或者界面库。
    或许你从未用过什么皮肤库,也不知道界面库怎么来的,有了这个介绍后,你应该就一清二楚了。而至于使用,dll会给一个函数你用,这个函数就是导出给你调用的。比如人家取名为RegisterGoodSkin(),你调用一下,然后按照他的说明,直接在CreateWindow时用他的窗口类名字就行了。他们还会把控件的窗口过程写好,你用的时候就和使用按钮控件一样。所以,使用第三方界面库,是比较简单的。原理也就是这样。
    我们C++技术网的课程最有价值的地方,就是这些基础原理,你学懂后,什么都很快就做得了,而且会越做越好。而不是其他人只是学怎么用,然后要死死的看别人的说明文档,一个字都不敢错,因为一错,就不知道怎么办了。而我们对此了如指掌了,最多就看别人提供的东西如何用,可以迅速上手,出问题也知道是什么问题,也可很快解决。
    这里说的是,你用一个dll加载到一个程序里面,作为这个程序的一部分,这样你的程序里就可以所以使用这些控件了。
    但是如果你想你写的控件,安装到一个系统后,所有进程都可以使用,这个还要额外做点事情哦。当然,这个你还是要在你的dll中注册全局窗口类。现在的问题就是,你要将你的dll加载到所有进程里面。你自己肯定无法做到,你不可能时刻去关注哪些进程启动了。那么这个工作就交给系统来完成。进程的创建都是由系统来创建的,所以,系统来做也是非常简单的。你要让系统帮你做这些事情,那就要告诉他你想这么做。
    那么你只需要在注册表中加一些信息,系统就知道了。系统要做什么,都回去问注册表的,注册表是Windows的核心数据库,各种各样的信息都记录在这里。我们要在注册表的如下注册表键设置一个值。:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows
     设置键AppInit_DLLs的值为你要注册的要让所有进程使用的全局窗口类的dll文件名。如果只填入一个dll文件名,那么系统会在系统目录中去找这个dll文件,所以你要将你的dll添加到系统目录中。比如windows和system等文件夹。
Windows零基础入门:2.10 三种窗口类的深入全面详细分析
    这样设置之后,任何进程启动的时候,系统都会在这个进程启动前,也就是在这个进程进入入口函数之前,就将指定的dll加载到这个进程的内存地址空间中,这样你的dll就成功入驻这个进程的内存了。那么你要做的事就是你的dll要初始化,初始化的时候,注册你的全局窗口类,这样,这个进程就可以使用你的进程了。
    根据前面讲过的创建窗口的时候查询窗口类的路线,局部窗口类->全局窗口类->系统窗口类。如果你dll注册的窗口类名和系统窗口类一样,那么这个程序使用的系统控件就被替换成你dll定义的样子了。这个功能就和系统换主题一样的效果,换了主题后,所有进程使用了标准控件的都成了主题中设定的空间样式一样了。
    看到了吧,就这个窗口类,你会发现,既懂了皮肤,也懂了主题。皮肤则只是一个进程内的,而主题可以是所有进程的范围,可以说是系统的皮肤了,那么这个就是系统的主题了呗。
    而应用程序局部的窗口类,则只是模块自己使用而已。比如主线程一个模块,叫做线程模块,还有各种dll插件,叫做插件模块。当然这里的模块名称的叫法可能和术语的不太一样,我也不去做学究,我只要知道是这么一个东西就好。那么各个模块不用CS_GLOBALCLASS类风格注册的窗口类就是局部的,只有模块内部可以使用。各个模块之间的局部窗口类是隔绝的,所以当然A模块和B模块的局部窗口类名字可以一样咯,反正互不侵犯,东村有个张三,西村也有个张三,因为两个村从来就是被喜马拉雅山隔开,一辈子也不会走到一起,不会冲突的。
    当然,在一个范围内肯定不能重复的,而局部窗口类->全局窗口类->系统窗口类这个层次,局部最优先。因为创建窗口时,最先从局部的开始找,找到后就不往后找了。这个层次之间,重名后,就会拦截优先级低的层次。这个拦截只是你这个窗口创建的时候,系统不往后找而已,并不会影响其他层次的窗口类的生活,另外一个进程依然可以正常使用全局或者系统窗口类。
    最后就要说一下修改和注销窗口类。前面说了,同一个层次内,不能有重复的窗口类。加入你用一个名字创建后,需要修改窗口类信息,可以使用SetWindowLong函数修改。修改的话,我在后续课程介绍。当然,嫌这个有点麻烦的话,你可以直接注销掉这个窗口类,然后再重新注册一个窗口类即可。注册窗口类就是向窗口类表中填一个信息,你当然可以修改,当然可以删掉。当然还可以再写。所以,这就是注册、修改、删除和重新注册咯。注销窗口类使用函数UnregisterClass即可,传入实例句柄(代表一个进程),传入一个类名,就可以了。很简单吧。当然,系统的窗口类你就别做梦想去注销了。别想在太岁头上动土哦。
    到此,我相信,对于窗口类的各种概念和背景,你应该完全明白了吧。至于修改窗口类和使用窗口类,就是接下来的课程内容啦。莫急,前面的都一一弄明白,后面会很简单的哦。