当前位置:首页 > 易语言相关 > 易语言教程资料 > 正文内容

《易语言3.0》中的Win32窗口子类化

Git开源网2018-09-30 20:36:43易语言教程资料1963

一、概念:

子类(Subclassing)是一种允许程序中途截获发往一个窗口的消息的一种技术。我们知道,Windows是基于消息的,因此,我们一旦截获了发往一个窗口的所有消息,就意味着我们能做一些通过常规方法无法做到的事。比如下面要讲到的,在窗口控制菜单上添加菜单项,并能有效地响应单击事件。

二、基础知识:

在Windows应用程序中,每个窗口(实际上是每类窗口)在创建后都有一个特殊的子程序——窗口程序(window procedure)来处理所有发往该窗口的所有消息。这个子程序如何响应消息也就决定了这个窗口如何动作。

我们要实现子类,要截获发给一个窗口的消息,就是要找到它的窗口程序,然后用我们自己的新窗口程序来替换,这样我们自己的新窗口程序就能接收到发给这个窗口的所有消息了!当然,我们也同时有了正确处理这些消息的责任。

在易语言中是看不到一个叫“_窗口1_窗口程序”的子程序的,易语言替我们实现并隐藏了这个细节。其实找出并替换它并不难,一个API就能搞定,它的声明是这样的:

Dll命令:置窗口特征

返回值类型:子程序指针

在Dll库中的命令名:SetWindowLongA

      参数:窗口句柄    数据类型:整数型

      参数:特征索引    数据类型:整数型

      参数:新特征      数据类型:子程序指针    备注:仅易语言3.0支持

这个函数有3个参数,第一个就是要操作的窗口的句柄,易语言中用“窗口名称.取窗口句柄 ()”来得到。

第二个参数是特征索引,因为这个API除了能替换窗口的“窗口程序”外还能设置指定窗口的其他许多特征,所以就需要这个参数来指明我们要改变的是窗口的哪个特征。在这里用常量 #窗口特征_窗口程序(其值是-4)就可以了。

第三个参数很显然要指明改变后的新窗口程序是哪个,可以用易语言3.0的操作符“&”来取得。易语言3.0以前版本虽然也支持子程序指针,但其值和API要求的值不兼容,所以我们是无法用易语言3.0以前的任何一个版本来实现子类。我用的是《易语言3.0测试版二》估计测《试版一》和以后的版本也应该没问题。

这个API的返回值是原来窗口程序的指针,要保存在一个容器里(假设保存到了一个名为 默认窗口程序 的“子程序指针”类型的容器里),当我们想取消子类化,或要关闭程序时,就应重新调用该API,置回原来的窗口程序。保存这个值的意义还不仅在此……暂时保密!!:)

 

 

好了,有了API这把厉刃,加上易语言3.0的强大功能,在概念上应该是没有问题了吧?那就开始吧,打开易语言3.0……慢!先别急,上边所讲的哪个API的第三个参数从哪里获得?取哪个子程序的指针呢?对了,我们还要编写一个新窗口程序。这个窗口程序其实是一个普通的易语言子程序,可以直接用易语言的“插入”菜单来插入一个“新子程序”,只是它的参数个数、类型和返回值的类型都有一定的格式,否则系统是不会让它作为窗口程序来处理窗口消息的(就像易语言中的“__启动窗口_创建完毕”一样,只是这个子程序的名字没有具体规定)。下面是一个例子:

 

 

子程序:新窗口程序

返回值类型:整数型

备注:不要修改该子程序的返回值及参数的类型!

 

参数:窗口句柄    数据类型:整数型

参数:消息        数据类型:整数型

参数:参数1       数据类型:整数型

参数:参数2       数据类型:整数型

 

我在一开始就说过,“得到与付出是成正比的”,既然系统把所有的发给原窗口程序处理的消息都发给了我们这个“新窗口程序”,那我们也要在这个子程序里正确处理所有接收到的消息。想一想吧,一个窗口从创建开始到被销毁要接收多少消息呢?!@#¥%^&×……“完了,上当了!!”别灰心,我并不是要你写代码来处理所有这些成千上百的烦人的消息,我的建议是这样的:我们在接收到消息后,处理自己感兴趣的那些,然后把其他那些“垃圾”全交给原来的窗口程序来处理好了!怎么样?这个建议还可以接受吧?

好了,再看一个API:

 

Dll命令:执行窗口程序

返回值类型:整数型

在Dll库中的命令名:CallWindowProcA

      参数:窗口程序程序指针  数据类型:子程序指针

      参数:窗口句柄    数据类型:整数型

      参数:消息        数据类型:整数型

      参数:参数1       数据类型:整数型

      参数:参数2       数据类型:整数型

 

和我们的“新窗口程序”的参数差不多!调用也简单得很。在我们刚才添加的“新窗口程序”里添上下面代码:(默认窗口程序 是置窗口特征()的返回值)

 

返回 (执行窗口程序 (默认窗口程序, 窗口句柄, 消息, 参数1, 参数2))

 *备注:把不需要处理的消息传递给默认窗口处理程序,使窗口能正常响应消息

 

这样我们把所有的消息原封不动地交给了它原来的“主人”,怎么样?放心了吧?也明白了为什么要保存 置窗口特征()的返回值了吧?现实中的做法通常是在“完璧归赵”之前或之后用“判断”语句来过滤出我们感兴趣的消息,进行处理,毕竟是我们费了这么一番周折才得到的消息,相信你绝对不会那么轻易地交回去的!:)

三、应用实例

忠告:调试这个程序要有充分的思想准备,容易死机!最好把易语言的自动保存时间设为1分钟,运行前先点保存按钮不失为明智之举。

 

讲了那么多乏味的东西,到底学这个有什么用呢?我们来看一个实例,我们要实现在窗口的“控制菜单”的底部加上一个明为“易语言万岁!”的菜单项,怎么样?见没见过别的程序有类似的效果?想不想知道它是怎么实现的?来,打开易语言3.0我们开始喽……

第一步:编写自己的窗口程序

 

子程序:新窗口程序

返回值类型:整数型

备注:不要修改该子程序的返回值及参数的类型!

 

参数:窗口句柄    数据类型:整数型

参数:消息  数据类型:整数型

参数:参数1       数据类型:整数型

参数:参数2       数据类型:整数型

 

如果真 (消息 = 274 且 参数1 = 1982)

 *备注:单击了控制菜单,并且菜单项的ID是我们设置的值

    信息框 (“易语言万岁!吴涛是我们的民族英雄!!”, 0, “支持国产精品---《易语言》!”)

如果真结束

 

返回 (执行窗口程序 (默认窗口程序, 窗口句柄, 消息, 参数1, 参数2))

 *备注:把不需要处理的消息传递给默认窗口处理程序,使窗口能正常响应消息

 

第二步:子类化及其恢复

 

子程序:__启动窗口_创建完毕

 

窗口句柄= _启动窗口.取窗口句柄 ()

系统菜单=取系统菜单 (窗口句柄, 假)

默认窗口程序=置窗口特征 (窗口句柄, #窗口特征_窗口程序, &新窗口程序)

 *备注:改变当前窗口的消息处理程序,即常说的“窗口子类化”

 

 

子程序:__启动窗口_将被销毁

 

局部容器:容器    数据类型:整数型

 

置窗口特征 (窗口句柄, -4, 默认窗口程序)

 *备注:窗口销毁前置回原来的消息处理程序

 

第三步:插入菜单项:

 

子程序:_添加控制菜单项_被单击

 

局部容器:结果    数据类型:逻辑型

 

结果=添加菜单项 (系统菜单, 7, 位或 (#菜单函数_按位置, #类型_分隔线), 253, “”)

 *备注:添加一条分隔线

结果=添加菜单项 (系统菜单, 8, #菜单函数_按位置, 1982, “易语言万岁!(&E)”)

 *备注:ID可以自己设,但要注意不要和系统已有的菜单项目ID重复,以免冲突

检查 (结果)

信息框 (“     只有想不到,没有做不到!” + #换行符 + #换行符 + “点窗口左上角的图标,看控制菜单的底部……”, #信息图标, “添加成功!”)

 

当然,我这里只是给出了程序的框架,要运行这个程序,有一些API要定义,比如“取系统菜单”、“添加菜单项”还有一些程序集容器要定义。你最好能到www.eyuyan.com的《易语言用户论坛》上下载我的源程序,里面还有如何实现动态菜单的例子和详细备注。

四、写在最后的话

(本应该写在最前面的)子类化的一条规则:只允许在同一进程内实行子类化,一个应用程序不能对属于其他进程的窗口实行子类化操作。

虽然这不是绝对的,但微软的工程师不推荐我们去子类化一个其他进程的窗口。这是因为在Win32中每一个的进程都拥有独立的地址空间,一个窗口的窗口程序在本进程中有一个特定的地址,但在其他进程中的同一地址里并不包含同样的窗口程序。强制替换的结果只有出错!

 

子类是一项很有用也很强大的技术,同时它的博大精深也让我肃然起敬,我这里提到的仅仅是子类的冰山一角(实例子类)还有诸如“全局子类”、“超类”等还有待我们去共同研究。建议参看MSDN中一篇题为《Safe Subclassing in Win32》的文章。

联系我:hyzs@sian.com

2003年4月1日


扫描二维码推送至手机访问。

版权声明:本文由Git开源网_git开源代码资源网_git开源博客发布,如需转载请注明出处。

本文链接:http://gitoscc.com/?id=30

相关文章

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。