"易语言.尘土"界面库2.0版源代码分析(3):窗口的消息处理函数(WndProc)(上)
作者:liigo
原文链接:http://blog.csdn.net/liigo/archive/2009/08/02/4401780.aspx
转载请注明出处:http://blog.csdn.net/liigo
上一篇提到,本篇的主要任务是找到“窗口的消息处理函数(WndProc)”。
Windows系统下的窗口一定有消息处理函数(WndProc),否则消息不能被处理,就不能称之为窗口了。一些系统定义的控件窗口,如button,edit,可以使用系统默认的消息处理函数,不影响用户在其父窗口的消息处理函数中子控件中消息,——这是因为,子控件的某些重要消息是通过WM_COMMAND,WM_NOTIFY发给它的父窗口的。Windows窗口系统这么设计的目的,也是为了避免要求用户为每一个窗口(包括子窗口)都明确给定一个消息处理函数,减小应用程序的开发复杂度。但无论如何,顶层窗口一定得有应用程序指定的消息处理函数(不执行任何操作的空白窗口除外)。
为窗口指定消息处理函数的一个常用的途径,是在调用Win32 API函数RegisterClass注册窗口类名(ClassName)时,通过参数WNDCLASS.lpfnWndProc指定消息处理函数。我们在"易语言.尘土"界面库2.0版源代码中全文搜索“RegisterClass”(All+E+E,详见上一篇),没有搜到,说明此界面库不是通过此途径指定窗口消息处理函数的。
那么我们需要回到上一篇提到的"_窗口基类"的"__监控窗口创建()"方法,看看它的具体实现,下面给出他的代码:
嗯,代码很简单,调用Win32 API函数SetWindowHookEx安装了一个WH_CBT类型的钩子,同时给该钩子指定了一个处理函数“__绑定对象和句柄”——哈哈,又是中文的函数名称,它的功能是什么,应该不用解释了吧,一会儿看到了代码,倒可以验证一下作者对此函数的命名是否准确到位。关于WH_CBT,它是一个与窗口有关的系统钩子(HOOK),Windows窗口系统会在窗口激活、创建、销毁、最小化/最大化、移动、改变大小时自动调用该钩的处理函数。详见MSDN中对WH_CBT的说明。下面给出此钩子的处理函数“__绑定对象和句柄”的易语言代码,看看它做了什么工作:
上面的代码,前两个判断用于过滤掉与窗口创建无关的HOOK通知,接下来的代码完成了三项任务:1、通过哈希表(hashmap)把窗口句柄和创建该窗口的易语言对象(准确的说,是对象变量的地址,该变量中存放的是对象内容的内存首地址(this指针))绑定在一起,将来可以通过窗口句柄查询到该窗口的易语言对象;2、修改窗口的消息处理函数(看,它终于出现了吧),所有窗口(包括顶层窗口和子窗口/标准控件)共同使用 EDuest_WndProc 这一个消息处理函数;3、卸载之前安装的钩子(下次创建另一个窗口时再次创建钩子,用完则卸载)。
可能有朋友会对第2项任务有疑问:所有窗口共用一个消息处理函数?那怎么区分是哪个窗口哪个易语言对象进而执行相关处理呢?答案是,在第1项任务中,已经把窗口句柄和易对象绑定在一起了,通过消息处理函数的窗口句柄参数可以直接查询到与之对应的易语言窗口对象。这个绑定,实际上是“结构化编程模式”向“面向对象编程模式”的一个转化;经过此转化,用户面对的就不再是赤裸裸的窗口句柄(HWND),而是经过高度抽象、有血有肉、有人情味的易语言窗口对象了;从一片荒蛮之地,来到了易语言的对象系统,生活就一下子丰富多彩起来,封装、继承、多态,终于可以大展身手了。
既然"易语言.尘土"界面库2.0版窗口的消息处理函数“EDust_WndProc”已经现出真身,下一步自然是要仔细分析它的内部实现了,可以预见,其中肯定隐藏了很多秘密,等候着我们去发掘。——当然,那已是下一篇的任务了,呵呵。
(补充说明一点细节,“__绑定对象和句柄”代码中有一段“天书”:置入代码 ({ 139, 93, 252, 139, 27, 131, 195, 4, 139, 69, 248, 137, 67, 8, 139, 69, 12, 137, 3 })。哇,这就是传说中的“置入代码”呀,C/C++代码中嵌入汇编代码算什么,易语言代码中可以直接嵌入机器代码(X86指令序列),比汇编语言更接近底层、更接近机器、更接近硬件,当然也更复杂更难懂。"易语言.尘土"界面库2.0版源代码中大量使用“置入代码”,其目的,一个是要更接近底层对象,一个是追求更高的执行效率。可见,一个库,为了上层用户的好用易用,是无所不用其极啊,良苦用心可见一斑。上面的“天书”,翻译成汇编代码如下图所示,再翻译成汉语大白话,其功能就是,把“设置新的消息处理函数之前的”原有的消息处理函数保存到窗口对象的第三个私有成员“旧窗口过程”(消息处理函数“EDust_WndProc”中要使用它),再把窗口句柄保存到窗口对象的第一个私有成员“_窗口句柄”(这样窗口对象也能知道与它绑定窗口句柄是何方高人了)。我们还发现,这一行代码有注释,简洁而准确。至于实现方式为什么如此曲折(为什么不直接赋值?),取决于易语言的表达方式,一言难尽,恕不多谈。)
(上图是“置入代码 ({ 139, 93, 252, 139, 27, 131, 195, 4, 139, 69, 248, 137, 67, 8, 139, 69, 12, 137, 3 })” 的等价汇编代码,需配合“__绑定对象和句柄”源代码上下文进行阅读。这里还涉及易语言对象的内存布局。普通用户不需要分心深究,以免走火入魔。)