跟着老侯玩编程 跟着老侯玩编程
首页
  • 基础语法
  • 网络编程
  • 设计模式
  • 基础篇
  • 进阶篇
  • 框架篇
  • Redis
  • Alibaba
  • 课程目录
  • 码农宝典
留言
首页
  • 基础语法
  • 网络编程
  • 设计模式
  • 基础篇
  • 进阶篇
  • 框架篇
  • Redis
  • Alibaba
  • 课程目录
  • 码农宝典
留言
  • 基础语法

    • 单元文件
    • 变量和常量
    • 内联变量
    • 基本数据类型
    • 复合数据类型
    • 语句(选择语句)
    • 语句(循环语句)
    • 数组
    • 初识例程
    • 初识面向对象
    • 属性Property
    • 面向对象的方法
    • 指针
    • 接口
    • 匿名函数和委托实现
    • 多态
    • 字符串详解
    • 异常处理
    • 反射机制
    • 泛型容器
    • JSON
    • 文件操作
    • Dll创建并调用
    • DLL初始化和退出处理
    • Package
    • 理解消息循环
    • VCL与Windows消息
      • VCL 类图的主要分支
      • TObject 与消息分发
      • TControl 与 Windows 消息的封装
    • 钩子原理
    • 进程通讯-内存映像
    • 多线程开篇
    • 线程控制
    • 线程同步
  • 网络编程

    • 网络编程基础
    • Delphi网络编程
    • select
    • WSAAsyncSelect
    • WSAEventSelect
  • 设计模式

    • 单例模式
    • 单例模式二
    • 策略模式
    • 简单工厂模式
    • 建造者模式
    • 原型模式
    • 模板方法模式
    • 状态模式
    • 迭代器模式
    • 解释器模式
    • 责任链模式
    • 中介者模式
    • 备忘录模式
    • 命令模式
    • 观察者模式
    • 访问者模式
    • 适配器模式
    • 桥接模式
  • Delphi
  • 基础语法
舞动的代码
2022-05-05
目录

VCL与Windows消息

VCL——Visual Component Library,是 Delphi 的基石。Delphi 的优秀,很大程度上得益于 VCL 的优秀。

VCL 是 Delphi 所提供的基本组件库,也就是所谓的 Application Framework,它对Windows API(应用程序接口)进行了全面封装,为桌面开发(不限于桌面开发)提供了整套的解决方案,使得程序员可以在不知晓 API 的情况下进行 Windows 编程。

不过,作为专业的程序员,不知晓 API 是不可能的。VCL 还是一个 Framework(应用程序框架),可以将 VCL 作为一个平台,程序员在其基础上构建应用程序,便可以忽略很多系统 API 的细节,而使得开发速度更快。

VCL 的组件也不同于 ActiveX 控件,VCL 组件通过源代码级连接到可执行文件中,因此其速度更快。而且,企业版的 Delphi 带有全部 VCL 库的源代码,这样程序员不单单可以知道如何使用 VCL 组件,更可以了解其运行机制与构架。

了解 VCL 的构架,无论对于编写自己的 Application,还是设计程序框架,或者创建自己的组件/类融入 VCL 构架中,都是必需和大有裨益的。这也符合某种规律:

在学习的时候,求甚解;

而在应用的时候,则寻找捷径。

Delphi和 VCL 都能满足这两种需求,因为使用它

  • 可以不隐藏任何想知道的细节;
  • 可以忽略不想知道的细节。

# VCL 类图的主要分支

mark

TObject 封装了 Object Pascal 类/对象的最基本行为。

TPersistent 派生自 TObject,TPersistent 使得自身及其派生类对象具有自我保存、持久存在的能力。

TComponent 派生自 TPersistent,这条分支之下所有的类都可以被称为“组件”。组件的一般特性是:

  1. 可出现在开发环境的“组件板”上。
  2. 能够拥有和管理其他组件。
  3. 能够存取自身(这是因为 TComponent 派生自 TPersistent)。

TControl 派生自 TComponent,其分支之下所有的类,都是在运行时可见的组件。

TWinControl 派生自 TControl,这个分支封装了 Windows 系统的屏幕对象,也就是一个真正的 Windows 窗口(拥有窗口句柄)。

TCustomControl 派生自 TwinControl。从 TCustomControl 开始,组件拥有了 Canvas(画布)属性。

# TObject 与消息分发

在 TObject 类中,有一个 Dispatch()方法和一个 DefaultHandler()方法,它们都是与消息分发机制相关的。

Dispatch()负责将特定的消息分发给合适的消息处理函数。首先它会在对象本身类型的类中寻找该消息的处理函数,如果找到,则调用它;如果没有找到而该类覆盖了 TObject 的 DefaultHandler(),则调用该类的 DefaultHandler();如果两者都不存在,则继续在其基类中寻找,直至寻找到 TObject 这一层,而 TObject 已经提供了默认的 DefaultHandler() 方法。

示例:

unit MsgDispTest;

interface

uses
  Dialogs, Messages;

type
  TMyMsg = record
    Msg: Cardinal;
    MsgText: ShortString;
  end;

  TMsgAccepter = class // 消息接收器类
  private
    procedure AcceptMsg2000(var msg: TMyMsg); message 2000;
    procedure AcceptMsg2002(var msg: TMyMsg); message 2002;
  public
    //默认处理函数
    procedure DefaultHandler(var Message); override;
  end;

implementation
{ TMsgAccepter }

procedure TMsgAccepter.AcceptMsg2000(var msg: TMyMsg);
begin
  ShowMessage('嗨,我收到了编号为 2000 的消息,它的描述是:' + msg.MsgText);
end;

procedure TMsgAccepter.AcceptMsg2002(var msg: TMyMsg);
begin
  ShowMessage('嗨,我收到了编号为 2002 的消息,它的描述是:' + msg.MsgText);
end;

procedure TMsgAccepter.DefaultHandler(var message);
begin
  ShowMessage('嗨,这个消息我不认识,无法接收,它的描述是:' + TMyMsg(message).MsgText);
end;

end.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

界面程序单元代码

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MsgDispTest;

type
  TForm1 = class(TForm)
    btnMsg2000: TButton;
    btnMsg2001: TButton;
    btnMsg2002: TButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnMsg2000Click(Sender: TObject);
    procedure btnMsg2002Click(Sender: TObject);
    procedure btnMsg2001Click(Sender: TObject);
  end;

var
  Form1: TForm1;
  MsgAccept: TMsgAccepter; // 自定义的消息接收类

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 // 创建 TMsgAccepter 类的实例
  MsgAccept := TMsgAccepter.Create();
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 // 析构 TMsgAccepter 类的实例
  MsgAccept.Free();
  MsgAccept := nil;
end;

procedure TForm1.btnMsg2000Click(Sender: TObject);
var
  Msg: TMyMsg;
begin
 // 将值为 2000 的消息分发给 MsgAccept 对象,观察其反应
  Msg.Msg := 2000;
  Msg.MsgText := 'Message 2000'; // 消息的文字描述
  MsgAccept.Dispatch(Msg); // 分发消息
end;

procedure TForm1.btnMsg2002Click(Sender: TObject);
var
  Msg: TMyMsg;
begin
 // 将值为 2002 的消息分发给 MsgAccept 对象,观察其反应
  Msg.Msg := 2002;
  Msg.MsgText := 'Message 2002'; // 消息的文字描述
  MsgAccept.Dispatch(Msg); // 分发消息
end;

procedure TForm1.btnMsg2001Click(Sender: TObject);
var
  Msg: TMyMsg;
begin
 // 将值为 2001 的消息分发给 MsgAccept 对象,观察其反应
  Msg.Msg := 2001;
  Msg.MsgText := 'Message 2001'; // 消息的文字描述
  MsgAccept.Dispatch(Msg); // 分发消息

end;

end.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

# TControl 与 Windows 消息的封装

TObject 提供了最基本的消息分发和处理的机制,而 VCL 真正对 Windows 系统消息的封装则是在 TControl 中完成的。

TControl 将消息转换成 VCL 的事件,以将系统消息融入 VCL 框架中。

调用关系

mark

TControl 组件类的声明

TControl = class(TComponent)
    Private
        FOnMouseDown: TMouseEvent;

        procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton; Shift: TShiftState);
        procedure MouseDown(Button: TMouseButton; Shift: TShiftState;X, Y: Integer); dynamic;
        procedure WMLButtonDown(var Message: TWMLButtonDown); message  WM_LBUTTONDOWN;
        procedure WMRButtonDown(var Message: TWMRButtonDown); message  WM_RBUTTONDOWN;
        procedure WMMButtonDown(var Message: TWMMButtonDown); message  WM_MBUTTONDOWN;
         //省略……
 protected
         //省略……
        property OnMouseDown: TMouseEvent read FOnMouseDown write  FOnMouseDown;
        //省略……
 end;
procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
begin
     SendCancelMode(Self);
     inherited;
    if csCaptureMouse in ControlStyle then
        MouseCapture := True;
    if csClickEvents in ControlStyle then
         Include(FControlState, csClicked);
    DoMouseDown(Message, mbLeft, []);
end;

procedure TControl.WMRButtonDown(var Message: TWMRButtonDown);
begin
    inherited;
    DoMouseDown(Message, mbRight, []);
end;

procedure TControl.WMMButtonDown(var Message: TWMMButtonDown);
begin
    inherited;
    DoMouseDown(Message, mbMiddle, []);
end;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

当 TObject.Dispatch()将 WM_LBUTTONDOWN 消息、WM_RBUTTONDOWN 消息或WM_MBUTTONDOWN 消息分发给 TControl 的派生类的实例后,WMLButtonDown()、WMRButtonDown()或 WMMButtonDown()被执行,然后它们都有类似这样 DoMouseDown(Message, mbRight, []); 的代码来调用 DoMouseDown():

procedure TControl.DoMouseDown(var Message: TWMMouse; Button:
TMouseButton; Shift: TShiftState);
begin
if not (csNoStdEvents in ControlStyle) then
    with Message do
    if (Width > 32768) or (Height > 32768) then
     with CalcCursorPos do
        MouseDown(Button, KeysToShiftState(Keys) + Shift, X,Y)
    else
         MouseDown(Button,KeysToShiftState(Keys) + Shift, Message.XPos, Message.Ypos);
end;
1
2
3
4
5
6
7
8
9
10
11

在 DoMouseDown()中进行一些必要的处理工作后(特殊情况下重新获取鼠标位置),就会调用 MouseDown():

procedure TControl.MouseDown(Button: TMouseButton;Shift: TShiftState; X, Y: Integer);
begin
    if Assigned(FOnMouseDown) then
        FOnMouseDown(Self, Button, Shift, X, Y);
end;
1
2
3
4
5

在 MouseDown()中,才会通过 FOnMouseDown 事件指针真正去执行用户定义的OnMouseDown 事件的代码。

由此,完成了 Windows 系统消息到 VCL 事件的转换过程。

备注:以上内容来源于【Delphi高手突破】一书

理解消息循环
钩子原理

← 理解消息循环 钩子原理→

Theme by Vdoing | Copyright © 2013-2023 冀ICP备16006233号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×