2004-08-20 17:31作者:NoteXPad出处:天极论坛 责任编辑:方舟
三、VCL 完整的消息分派流程
1. TButton
新建一个 Application,在 Form1 上放一个 Button (缺省名为Button1),在其 OnClick 事件中随便写点代码,加上断点,在调试之前,请打开 DCU 调试开关(Project->Options->Compiler->Use Debug DCUs), 这个开关如果不打开,是没法调试 VCL 的,然后 F9 运行,当停留在断点上时,打开Call Stack 窗口(View->Debug Window->Call Stack)可看到调用顺序如下(从底往上看):
TForm1.Button1Click($9637C0) TControl.Click TButton.Click TButton.CNCommand((48401, 660, 0, 524948, 0)) TControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TWinControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TButtonControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TControl.Perform(48401,660,524948) DoControlMsg(524948,(no value)) TWinControl.WMCommand((273, 660, 0, 524948, 0)) TCustomForm.WMCommand((273, 660, 0, 524948, 0)) TControl.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TWinControl.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TCustomForm.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TWinControl.MainWndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) StdWndProc(918056,273,660,524948) TWinControl.DefaultHandler((no value)) TControl.WMLButtonUp((514, 0, 48, 13, (48, 13), 0)) TControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) TWinControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) TButtonControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) TWinControl.MainWndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) StdWndProc(524948,514,0,852016) TApplication.HandleMessage TApplication.Run Project1 |
一个 Button 被点击,在 TButton 内部会发生两个消息:WM_LBUTTONDOWN/WM_LBUTTONUP, TButton 没有处理 WM_LBUTTONUP(问题:为什么只响应 WM_LBUTTONUP,这两个消息只应该发生在 Windows 原生控件内,除非 TButton subclass 了 "Button",这部分代码我没看),只是交给 TWinControl.DefaultHandler,随后 TButton 又将生成的 WM_COMMAND 消息发送给它的 Parent,即 TForm,经过一系列消息传递, WM_COMMAND 在 TWinControl.WMCommand 中被处理,通过 DoControlMsg 将 WM_COMMAND 加工成 CN_COMMAND,再利用 TControl.Perform 将 CN_COMMAND 传回 TButton,又通过一系列的消息传递到 TButton 中的 Dispatch,通过查询动态方法表找到 Handler -- TButton.CNCommand,它又调用虚方法 TButton.Click,继而调用 TControl.Click,在这个方法中会调用 FOnClick,而 FOnClick 特性值的内容就是当程序员使用对象查看器撰写 TButton 的 OnClick 事件处理函数时 Delphi 便会自动指定给 TButton 的 OnClick 特性,例子中 OnClick 被指定为 TForm1.Button1Click,因此 TForm1.Button1Click 最终被调用。
2. TForm
新建一个 Application,为 Form1 的 OnMouseDown 事件随便写一点代码,在这个方法上设断点,F9 运行,看看 Call Stack
TForm1.FormMouseDown(???,???,[ssLeft],346,212) TControl.MouseDown(mbLeft,[ssLeft],346,212) TControl.DoMouseDown((513, 1, 346, 212, (346, 212), 0),mbLeft,[]) TControl.WMLButtonDown((513, 1, 346, 212, (346, 212), 0)) TControl.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) TWinControl.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) TCustomForm.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) TWinControl.MainWndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) StdWndProc(2687598,513,1,13893978) TApplication.HandleMessage TApplication.Run Project1 |
鼠标在 Form 上点击,产生两个消息 WM_LBUTTONDOWN/WM_LBUTTONUP,但我们只截获 WM_LBUTTONDOWN。产生的 WM_LBUTTONDOWN 经过一系列的消息传递到达 TObject.Dispatch,通过查询动态方法表在 TForm 的父类 TControl 中找到了 Handler -- TControl.WMLButtonDown,在 TControl.WMLButtonDown 中又经过 TControl.DoMouseDown、TControl.MouseDown 一系列方法调用,最终调用到 FOnMouseDown,FOnMouseDown 被赋值为 TForm1.FormMouseDown,调用 FOnMouseDown 即调用 TForm1.FormMouseDown。
讲了一大堆消息实现过程,那么在实际中到底有哪些应用?