别人造砖我砌房!
Delphi 高手突破
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 库的核心,剖析 VCL 的代码。从此,VCL 对您来说不会再
是神秘而艰涩的,因为带领读者它们同样是用代码铸造成的。
4.1 VCL 概 貌
先看一下 VCL 类图的主要分支,如图 4.1 所示。
在图中可以看到,TObject 是 VCL 的祖先类,这也是 Object Pascal 语言所规定的。但
实际上,TObject 以及 TObject 声明所在的 system.pas整个单元,包括在“编译器魔法”话
题中提到的_ClassCreate等函数,都是编译器内置支持的。因此,无法修改、删除 system.pas
中的任何东西,也无法将 system.pas 加入你的 project,否则会得到“Identifier redeclared
‘system’”的错误提示,因 project 中已经被编译器自动包含了 system单元。
意思是,TObject 是 Object Pascal 语言/编译器本身的一个性质! 注意:TObject 是属于编译器的特性!
TObject 封装了 Object Pascal 类/对象的最基本行为。
TPersistent 派生自 TObject,TPersistent 使得自身及其派生类对象具有自我保存、持久
存在的能力。
TComponent派生自 TPersistent,这条分支之下所有的类都可以被称为“组件”。组件
的一般特性是:
(1)可出现在开发环境的“组件板”上。
·66·
VCL 库
TObject
…… TRegistry TPersistent
4
TStrings TComponent
TStringList TApplication TControl
TGraphicControl TWinControl
TCustomControl
图4.1 VCL 类图主要分支(深色表示核心分支)
(2)能够拥有和管理其他组件。
(3)能够存取自身(这是因为 TComponent 派生自 TPersistent)。
TControl 派生自 TComponent,其分支之下所有的类,都是在运行时可见的组件。
TWinControl 派生自 TControl,这个分支封装了 Windows 系统的屏幕对象,也就是一
个真正的 Windows窗口(拥有窗口句柄)。
TCustomControl 派生自 TwinControl。从 TCustomControl 开始,组件拥有了 Canvas(画
布)属性。
从 4.2 节开始,将会先后结合 VCL 中一些核心类的实现代码来了解它们。
4.2 TObject 与消息分发
首先来看一下 TObject 这个“万物之源”究竟长得何等模样。它的声明如下:
TObject = class constructor Create; procedure Free; class function InitInstance(Instance: Pointer): TObject; procedure CleanupInstance; function ClassType: TClass;
·67·
Delphi 高手突破
class function ClassName: ShortString;
class function ClassNameIs(const Name: string): Boolean;
class function ClassParent: TClass;
class function ClassInfo: Pointer;
class function InstanceSize: Longint;
class function InheritsFrom(AClass: TClass): Boolean;
class function MethodAddress(const Name: ShortString): Pointer;
class function MethodName(Address: Pointer): ShortString;
function FieldAddress(const Name: ShortString): Pointer;
function GetInterface(const IID: TGUID; out Obj): Boolean;
class function GetInterfaceEntry(const IID: TGUID):
PInterfaceEntry;
class function GetInterfaceTable: PInterfaceTable;
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer): HResult; virtual;
procedure AfterConstruction; virtual;
procedure BeforeDestruction; virtual;
procedure Dispatch(var Message); virtual;
procedure DefaultHandler(var Message); virtual;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
end;
从 TObject 的声明中可以看到,TObject 包含了诸如实例初始化、实例析构、RTTI、消
息分发等相关实现的方法。现在就来研究一下TObject与消息分发,这也是VCL对Windows
消息封装的模型基础。
在 TObject 类中,有一个 Dispatch()方法和一个 DefaultHandler()方法,它们都是与消息
分发机制相关的。
Dispatch()负责将特定的消息分发给合适的消息处理函数。首先它会在对象本身类型
的类中寻找该消息的处理函数,如果找到,则调用它;如果没有找到而该类覆盖了 TObject
的 DefaultHandler(),则调用该类的 DefaultHandler();如果两者都不存在,则继续在其基
类中寻找,直至寻找到 TObject 这一层,而 TObject 已经提供了默认的 DefaultHandler()
方法。
先来看一个示例程序,它演示了消息分发及处理的过程。该程序的代码及可执行文件
可在配书光盘的 MsgDisp 目录下找到。
首先自定义一个消息结构 TMyMsg,它是我们自定义的消息记录类型。对于自定义的
消息类型,VCL 只规定它的首 4 字节必须是消息编号,其后的数据类型任意。同时,VCL
也提供了一个 TMessage类型用于传递消息。在此程序中,不使用 TMessage,而用 TMyMsg
代替:
·68·