Delphi 的消息机制浅探.doc

上传人:人*** 文档编号:548210413 上传时间:2023-12-25 格式:DOC 页数:32 大小:149KB
返回 下载 相关 举报
Delphi 的消息机制浅探.doc_第1页
第1页 / 共32页
Delphi 的消息机制浅探.doc_第2页
第2页 / 共32页
Delphi 的消息机制浅探.doc_第3页
第3页 / 共32页
Delphi 的消息机制浅探.doc_第4页
第4页 / 共32页
Delphi 的消息机制浅探.doc_第5页
第5页 / 共32页
点击查看更多>>
资源描述

《Delphi 的消息机制浅探.doc》由会员分享,可在线阅读,更多相关《Delphi 的消息机制浅探.doc(32页珍藏版)》请在金锄头文库上搜索。

1、Delphi 的消息机制浅探 2004.1.9我从去年 12 月上旬开始等待李维的Inside VCL。我当时的计划是,在这本书的指导下深入学习 Delphi。到了 12 月底,书还没有出来,我不愿再等,开始阅读 VCL 源代码。在读完 TObject、TPersistant 和 TComponent 的代码之后,我发现还是不清楚 Delphi 对象到底是怎样被创建的。于是我查看 Delphi 生成的汇编代码,终于理解了对象创建的整个过程(这里要特别感谢 book523 的帮助)。此后我就开始学习 Delphi VCL 的消息处理机制。自从我写下Delphi的对象机制浅探,至今正好一个星期,我

2、也基本上把 Delphi VCL 的消息处理框架读完了。我的学习方法就是阅读源代码,一开始比较艰苦,后来线索逐渐清晰起来。在此把自己对 Delphi VCL 消息机制的理解记录下来,便于今后的复习,也给初学 Delphi 或没有时间阅读 VCL 源代码的朋友参考(毕竟没有几个程序员像我这样有时间 :)。由于学习时间较短,一定会有错误,请大家指正。我在分析 VCL 消息机制的过程中,基本上只考查了三个类 TObject、TControl 和 TWinControl。虽然我没有阅读上层类(如 TForm)的代码,但我认为这些都是实现的细节。我相信 VCL 消息系统中最关键的东西都在这三个类中。纲举

3、而目张,掌握基础类的消息处理方法之后再读其他类的消息处理过程就容易得多了。要想读懂本文,最低配置为:了解 Win32 消息循环和窗口过程基本了解 TObject、TControl 和 TWinControl 实现的内容熟悉 Delphi 对象的重载与多态推荐配置为: 熟悉 Win32 SDK 编程 熟悉 Delphi 的对象机制 熟悉 Delphi 内嵌汇编语言推荐阅读: Delphi 的原子世界 http:/ VCL窗口函数注册机制研究手记,兼与MFC比较 http:/ Delphi的对象机制浅探 http:/ 正文由窗口自动换行;所有代码以 80 字符为边界;中英文字符以空格符分隔。 (作

4、者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。)目 录 一个 GUI Application 的执行过程:消息循环的建立 TWinControl.Create、注册窗口过程和创建窗口 补充知识:TWndMethod 概述 VCL 的消息处理从 TWinControl.MainWndProc 开始 TWinControl.WndProc TControl.WndProc TObject.Dispatch TWinControl.DefaultHandler TControl.Perform 和 TWinControl.Broadcast TWinControl.WMPaint 以

5、 TWinControl 为例描述消息传递的路径正 文 一个 GUI Application 的执行过程:消息循环的建立通常一个 Win32 GUI 应用程序是围绕着消息循环的处理而运行的。在一个标准的 C 语言 Win32 GUI 程序中,主程序段都会出现以下代码:while (GetMessage(&msg, NULL, 0, 0) / GetMessage 第二个参数为 NULL, / 表示接收所有应用程序产生的窗口消息 TranslateMessage(&msg); / 转换消息中的字符集 DispatchMessage(&msg); / 把 msg 参数传递给 lpfnWndProc

6、lpfnWndProc 是 Win32 API 定义的回调函数的地址,其原型如下:int _stdcall WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); Windows 回调函数(callback function) 也通常被称为窗口过程(window procedure),本文随意使用这两个名称,代表同样的意义。应用程序使用 GetMessage 不断检查应用程序的消息队列中是否有消息到达。如果发现了消息,则调用 TranslateMessage。TranslateMessage 主要是做字符消息本地化的工作,不是关

7、键的函数。然后调用 DispatchMessage(&msg)。DispatchMessage(&msg) 使用 msg 为参数调用已创建的窗口的回调函数(WndClass.lpfnWndProc)。lpfnWndProc 是由用户设计的消息处理方法。当 GetMessage 在应用程序的消息队列中发现一条 WM_QUIT 消息时,GetMessage 返回 False,消息循环才告结束,通常应用程序在这时清理资源后也结束运行。使用最原始的 Win32 API 编写的应用程序的执行过程是很容易理解的,但是用 Delphi VCL 组件封装消息系统,并不是容易的事。首先,Delphi 是一种面向

8、对象的程序设计语言,不但要把 Win32 的消息处理过程封装在对象的各个继承类中,让应用程序的使用者方便地调用,也要让 VCL 组件的开发者有拓展消息处理的空间。其次,Delphi 的对象模型中所有的类方法都是对象相关的(也就是传递了一个隐含的参数 Self),所以 Delphi 对象的方法不能直接被 Windows 回调。Delphi VCL 必须用其他的方法让 Windows 回调到对象的消息处理函数。让我们跟踪一个标准的 Delphi Application 的执行过程,查看 Delphi 是如何开始一个消息循环的。program Project1;begin Application.I

9、nitialize; Application.CreateForm(TForm1, Form1); Application.Run;end. 在 Project1 的 Application.Initialize 之前,Delphi 编译器会自动插入一行代码:SysInit._InitExe。_InitExe 主要是初始化 HInstance 和模块信息表等。然后 _InitExe 调用 System._StartExe。System._StartExe 调用 System.InitUnit;System.InitUnit 调用项目中所有被包含单元的 Initialization 段的代码;其

10、中有 Controls.Initialization 段,这个段比较关键。在这段代码中建立了 Mouse、Screen 和 Application 三个关键的全局对象。Application.Create 调用 Application.CreateHandle。Application.CreateHandle 建立一个窗口,并设置 Application.WndProc 为回调函数(这里使用了 MakeObjectInstance 方法,后面再谈)。Application.WndProc 主要处理一些应用程序级别的消息。我第一次跟踪应用程序的执行时没有发现 Application 对象的创建过

11、程,原来在 SysInit._InitExe 中被隐含调用了。如果你想跟踪这个过程,不要设置断点,直接按 F7 就发现了。然后才到了 Project1 的第 1 句: Application.Initialize;这个函数只有一句代码:if InitProc nil then TProcedure(InitProc);也就是说如果用户想在应用程序的执行前运行一个特定的过程,可以设置 InitProc 指向该过程。(为什么用户不在 Application.Initialize 之前或在单元的 Initliazation 段中直接运行这个特定的过程呢?一个可能的答案是:如果元件设计者希望在应用程序

12、的代码执行之前执行一个过程,并且这个过程必须在其他单元的 Initialization 执行完成之后执行比如说 Application 对象必须创建,则只能使用这个过程指针来实现。)然后是 Project1 的第 2 句: Application.CreateForm(TForm1, Form1);这句的主要作用是创建 TForm1 对象,然后把 Application.MainForm 设置为 TForm1。最后是 Project1 的第 3 句: Application.Run;TApplication.Run 调用 TApplication.HandleMessage 处理消息。Appl

13、ication.HandleMessage 的代码也只有一行:if not ProcessMessage(Msg) then Idle(Msg);TApplication.ProcessMessage 才真正开始建立消息循环。ProcessMessage 使用 PeekMessage API 代替 GetMessage 获取消息队列中的消息。使用 PeekMessage 的好处是 PeekMessage 发现消息队列中没有消息时会立即返回,这样就为 HandleMessage 函数执行 Idle(Msg) 提供了依据。ProcessMessage 在处理消息循环的时候还特别处理了 HintMs

14、g、MDIMsg、KeyMsg、DlgMsg 等特殊消息,所以在 Delphi 中很少再看到纯 Win32 SDK 编程中的要区分 Dialog Window、MDI Window 的处理,这些都被封装到 TForm 中去了(其实 Win32 SDK 中的 Dialog 也是只是 Microsoft 专门写了一个窗口过程和一组函数方便用户界面的设计,其内部运作过程与一个普通窗口无异)。function TApplication.ProcessMessage(var Msg: TMsg): Boolean;var Handled: Boolean;begin Result := False; if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then / 从消息队列获取消息 begin Result := True; if Msg.Message WM_QUIT then begin Handled := False; / Handled 表示 Application.OnMessage 是否已经处理过 / 当前消息。 / 如果用户设置了Application.OnMessage 事件句柄,

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 生活休闲 > 科普知识

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号