科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件深入理解Delphi的消息机制

深入理解Delphi的消息机制

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

永远记住,无论你是用 SDK 还是借用 VCL 来创建窗口,都要遵循 Windows 的游戏规则,即先注册窗口类,然后再创建窗口实例。

作者:NoteXPad 来源:天极论坛 2007年10月31日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
三、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。
讲了一大堆消息实现过程,那么在实际中到底有哪些应用?
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章