本书通过对Delphi内核(RTL)源代码进行分析,深入阐述了Delphi内核(RTL)的原理及其实现。全书从Nico Bendlin编写的著名最小化内核示例程序MiniDExe开始讲起,基于MiniDExe分析Delphi在编译器一级的技术内幕,带领读者一窥Delphi的核心。随后作者基于这个内核逐层地包装代码,将Delphi的各种功能的具体实现一一展现,通过列出关键性代码并进行系统性分析的方式,全面分析各种操作系统运行环境机制在源代码中的实现。全书内容详实,阐述精辟、深入,主要议题包括Delphi的编译器在Windows、Delphi RTL和用户代码之间的交互;Delphi RTL内核代码的完整实现;与Delphi内核相关的操作系统机制;初始(入口)代码、模块、内存、线程、资源、异常处理机制等;是一本不可多得的高端技术图书。适合中、高级Delphi开发人员研读。
第一部分 DELPHI内核深入剖析(I) 1
第1章 最小化DELPHI内核 3
1.1 MiniDExe如何实现内核最小化 3
1.1.1 MiniDExe中的System.pas单元 4
1.1.2 MiniDExe中的SysInit.pas单元 5
1.1.3 MiniDExe中的项目文件MiniDExe.dpr 6
1.2 一些其他的内核优化 6
1.3 为什么要研究最小化内核 7
第2章 基本数据类型的实现 9
2.1 基本数据类型 9
2.2 变量与常量 11
2.2.1 全局变量与局部变量 11
2.2.2 动态分配的内存 12
2.2.3 换一个方式来理解 13
2.2.4 常量 14
2.3 数据结构的实现 16
2.3.1 简单类型 16
2.3.2 字符串 16
2.3.3 构造类型 26
2.3.4 指针类型 28
2.3.5 过程类型 28
2.4 数据结构相关的例程 29
2.4.1 标准Pascal的内置例程 30
2.4.2 字符串操作例程 31
2.5 变量的类型检测与强制转换 47
2.6 引用-计数-写复制与类型信息 48
2.6.1 引用计数与增加引用 48
2.6.2 “增加引用”何时发生 49
2.6.3 增加引用的操作是依赖类型信息来实现的 51
2.6.4 写复制与值参数的备份 53
第3章 BASM(BORLAND汇编语言)精要 55
3.1 BASM概念简要 55
3.2 表达式的类别与类型 56
3.3 数据定义和数据类型强制转换 57
3.4 例程入口参数及调用约定 61
3.5 例程和API的调用与流程控制 62
3.6 完全汇编例程与内嵌汇编例程 65
3.7 汇编例程中的返回值约定 66
3.8 其他 67
第4章 初始化与结束化过程 71
4.1 变量的初始化与结束化 71
4.1.1 初始化的必要性 72
4.1.2 如何初始化 73
4.1.3 如何结束化 74
4.2 例程的初始化与结束化 76
4.3 单元初始化与结束化 77
4.4 模块初始化与结束化 79
4.4.1 模块入口代码 79
4.4.2 编译器决定的程序执行流程 80
第5章 面向WINDOWS开发的基本实现 83
5.1 Win32应用程序:EXE 83
5.1.1 适应Win32应用程序的最简化内核 83
5.1.2 初始化例程_InitExe() 85
5.1.3 内部模块表管理例程 86
5.1.4 .EXE启动例程_StartExe() 87
5.1.5 应用程序的结束化控制 88
5.2 32位的DOS:控制台应用程序 91
5.2.1 控制台应用程序的模块入口代码 91
5.2.2 控制台应用程序的最小化实现 92
5.2.3 控制台应用程序的Delphi实现 93
5.2.4 文件操作例程与控制台应用程序 94
5.2.5 控制台的开启与关闭 97
5.2.6 CRT单元与Input、Output的重载 98
5.3 动态链接库:DLL 99
5.3.1 丢失的DllMain() 99
5.3.2 _InitLib()例程 101
5.3.3 _StartLib()例程 101
5.3.4 .DLL的结束化过程 102
5.3.5 DllProc与DllMain()的不同 105
5.3.6 动态链接库的内核最小化 106
5.4 Delphi的动态链接库:包 108
5.4.1 包的主要规则 108
5.4.2 Delphi中的包与普通DLL的区别 110
5.4.3 包的DllMain() 111
5.4.4 包的载入例程LoadPackage() 112
5.4.5 真正的初始化例程InitializePackage() 112
5.4.6 包的卸载例程UnloadPackage() 115
5.4.7 包的基本输出例程 115
5.4.8 内部例程_PackageLoad()与_PackageUnload() 116
5.4.9 包的内核最小化 117
5.5 其他 118
5.5.1 初始化上下文中OuterContext域的使用 118
5.5.2 入口代码中的堆栈使用深入分析(内存现场) 119
5.5.3 再论入口代码 123
第6章 DELPHI的积木艺术(PE) 127
6.1 PE文件结构概要 127
6.1.1 文件头 129
6.1.2 节表 131
6.1.3 节 132
6.1.4 PE文件与内存映射 134
6.1.5 有关相对虚地址的计算 138
6.2 Delphi的PE文件头中一些重要的域 142
6.3 Delphi的PE文件中一些重要的节 144
6.3.1 线程局部存储(.tls和.rdata) 144
6.3.2 资源节(.rsrc) 145
6.3.3 导入、导出表(.idata和.edata) 146
6.3.4 数据节与代码节(DATA、BSS和CODE) 154
6.3.5 重定位节(.reloc) 157
6.4 3.5K的秘密 159
6.4.1 Delphi的PE文件头部 159
6.4.2 Delphi的PE文件的节及其默认对齐 160
6.4.3 还可能更小吗 160
6.4.4 3.5K代码的内存映射 161
6.5 入口点 162
第二部分 DELPHI内核深入剖析(II) 167
第7章 DELPHI的内存管理器 169
7.1 Delphi的内存管理器实现框架 169
7.2 内存页管理 171
7.3 堆 172
7.4 MemoryManager及相关例程 172
7.5 GetMem.inc中的重要例程 174
7.5.1 堆块及其管理例程 175
7.5.2 虚地址空间(Address space)管理 178
7.5.3 已提交的内存空间(Committed space)管理 179
7.5.4 用户调用例程(actually calls)的实现 180
7.5.5 初始化、结束化与其他辅助例程 194
7.6 遍历全部内存块 195
7.7 共享内存管理器 197
7.8 第三方内存管理器 200
7.9 小结 201
第8章 错误和异常 203
8.1 错误 203
8.2 断言 206
8.3 Windows与Delphi中的异常处理机制概要 209
8.4 编译器对异常处理机制的实现 212
8.4.1 最小化内核的启示 212
8.4.2 从操作系统的角度来理解编译器行为 216
8.4.3 try..finally/except..end语法关键字与内部例程 219
8.5 基本(except型)异常处理 221
8.5.1 异常触发(Raise) 221
8.5.2 多层(嵌套)的异常处理 222
8.5.3 异常展开(Unwind) 223
8.5.4 异常响应(Notify) 227
8.5.5 顶层异常处理 229
8.6 使用面向对象技术的异常类 233
8.6.1 异常列表、RaiseFrame与ExceptionRecord 233
8.6.2 未知异常映射:ExceptObjProc 235
8.6.3 不使用SysUtils.pas单元的应用程序 236
8.6.4 _RaiseExcept()、_RaiseAgain()与_DoneExcept()例程 238
8.6.5 异常对象 241
8.6.6 异常类型检测的依赖性问题 247
第9章 多线程的实现及调度 250
9.1 操作系统的进程与线程 251
9.1.1 线程的启动过程 252
9.1.2 主线程与用户线程启动过程的比较 254
9.1.3 线程有哪些独立元素 255
9.2 操作系统的线程调度概要 256
9.3 基本的多线程应用 257
9.4 线程变量(线程局部存储) 260
9.4.1 线程局部存储(TLS)的实质 261
9.4.2 编译器的准备工作 262
9.4.3 静态TLS:EXE模块中的TLS 265
9.4.4 动态TLS:DLL模块中的TLS 266
9.5 再论FS段 268
9.6 线程类(TThread) 271
9.6.1 线程执行体(Execute) 275
9.6.2 线程与主线程的同步(Synchronize) 277
9.6.3 控制台应用的多线程同步(第三方的WakeMainThread例程) 287
9.6.4 线程中止和结束(Terminate) 291
9.7 线程同步对象 297
9.7.1 线程同步方法 298
9.7.2 Delphi中的线程同步类 306
9.7.3 线程对象的WaitFor()方法 311
9.8 多线程环境下的内存管理器 312
第10章 资源的管理和使用 315
10.1 资源字符串 315
10.1.1 隐含的调用:LoadResString()例程 315
10.1.2 _InitResStrings()例程 317
10.1.3 _InitResStringImports()例程 319
10.1.4 _InitImports()例程 320
10.2 Delphi编译的PE文件的默认资源 322
10.2.1 PACKAGEINFO资源 323
10.2.2 PACKAGEOPTIONS资源 324
10.2.3 DVCLAL资源 326
10.2.4 DESCRIPTION资源 327
10.3 其他 327
第11章 突破DELPHI在应用开发上的限制 329
11.1 开发DOS保护模式程序 329
11.2 开发Windows9x上的驱动程序 335
11.2.1 Delphi编译的.OBJ文件 335
11.2.2 第三方的链接器处理Delphi的.OBJ文件的问题 336
11.2.3 在Delphi中编写VxD驱动程序代码 337
11.2.4 链接目标文件并输出VxD驱动程序 338
11.3 开发WinNT/2000上的驱动程序 339
11.4 其他 342
术语表 343
参考书目 347
第一次和Aimingoo见面是2003年底Borland Conference 2003在北京举行之际,Aimingoo宏亮的声音,诚恳的态度立刻吸引了我的好感。在BorCon排练时Aimingoo努力不懈一直坚持到最后,我当时心中就想这位年轻人真是不错。到了BorCon结束之后,我和Aimingoo有更多机会可以聊天,让我了解了Aimingoo的专长领域,也很佩服Aimingoo这么年轻就拥有这么多的开发经验。 在知道Aimingoo要写有关Delphi Run Time Library的书时,心中又惊又喜。惊的是这可是一个浩大的工程,喜的则是Aimingoo愿意花时间写这方面的书籍,造福想要一窥究竟的Delphi使用者。当Aimingoo把他的书籍目录和初稿给我过目时我又不禁羡慕他的成果以及Aimingoo的书籍和拙作Inside VCL是如此的相辅相成,Aimingoo的书籍叙述了驱动Delphi应用程序的引擎动力,Inside VCL则说明了Delphi应用程序使用的系统架框。 Aimingoo请我为他的新书写序实在是我的荣幸,除了他丰富的知识和精湛的技术之外, Aimingoo做事的态度更令人敬佩。结合技术和努力的结晶,Aimingoo为广大的Delphi使用者写出了另一本Delphi领域的著作,对于想要深入了解Delphi执行时期运作细节的Delphi使用者而言,这是值得阅读和珍藏的一本好书。 最后我个人也要谢谢Aimingoo又为李维工作室产生了一本高质量的技术书籍,让李维工作室系列书籍更为丰富和充实。更希望Aimingoo能够继续写出好书,不断帮助我们这群Delphi使用者更上一层楼。
关于本书在用Delphi1.0时,我便开始阅读Delphi的源代码了。大约是在五年前,我曾试图做一个名为“Delphi源代码深入分析”的网站,后来终于放弃了这个计划。而自此,写现在这样一本书的想法便积蓄至今。真正激发我做这样一件事的,是Nico Bendlin写的MiniDExe。这是我所见过的用Delphi写的最小版本的可执行程序,它使我得以一窥Delphi的核心。于是,我开始基于MiniDExe分析Delphi在编译器一级上的真相。随后,我分析了对象结构、VCL和COM等在源代码中的实现。至此,本书的基本知识框架组织完成。本书的知识结构
本书以内核的原理及其实现为主,主要讲述:l Delphi的编译器在Windows、Delphi RTL和用户代码之间的交互;l Delphi RTL内核代码的完整实现;l 与Delphi内核相关的操作系统机制;l 初始(入口)代码、模块、内存、线程、资源、异常处理机制等。所有在本书中使用到的工具、术语、参考资料等将以附录的形式列出。本书基于Delphi 7.0所提供的源程序分析。什么是“内核” 通常意义上的开发工具“内核”,指的是RTL(RunTime Library)。在Delphi中,RTL是指源码的$(Source)\RTL目录中的全部代码。但以纯粹的RTL的概念来理解的话,事实上它应当是指System.pas和SysInit.pas中的全部代码(包括GetMem.inc)。——这甚至不包括SysUtils.pas这个单元。在本书中所讨论的内核,基本上是指$(Source)\RTL\Sys目录中的代码。除此之外,还包括:l $(Source)\RTL\Win:Windows.pas的部分代码。l $(Source)\RTL\Common:Classes.pas和SyncObjs.pas中的部分代码。如何阅读这本书 写这本书的过程中,我几乎无时不在问自己“Delphi如何实现这个”或者“Delphi如何实现那个”。所以这本书在很大程度上都是在描述“Delphi如何做到”,而不是“用Delphi如何做”。因此,我建议你以技术探索的角度来读这本书。你不要指望在本书中发现太多能直接在工程中使用的技术与技巧。本书虽然是立足于Delphi源代码分析,但书中并不会大段地给出Delphi产品中的源码。因为Borland并没有这样的授权。通常情况下,我会列出关键性的代码,并进行系统的分析。因此,如果需要,读者可能必须自行对照源码中的.pas文件来阅读一些章节。本书不包含内核源码的哪些内容 本书立足于对Delphi内核(RTL)源代码的深入分析,但为了叙述的必要,仍剔除了部分内容。这些内容主要是指:一、 BCB兼容代码我不是太了解BCB。因此除非必要,我不会在本书的章节中讨论与BCB相关的内容。基于同样的原因,在Delphi内核源码中与BCB相关的(绝大多数)代码被我忽略了。二、 调试代码除了在讲述异常机制时,其它绝大多数情况下我都不会讨论在Delphi内核源码中与调试相关的代码。那的确是相当复杂的一套代码。不过在读完本书之后,我相信读者已经有能力自行开始分析这些代码了。三、 面向Linux的移植:CLX要讨论CLX,则必须进一步深入了解Kylix和Linux操作系统。如果在本书中大量篇幅出现这些内容,将会降低本书在主流的桌面开发平台(当然,你知道我指的是什么)上的实用性,同时,也将使关于RTL的分析显得杂乱。本书的示例源码 我绝不想用大量源码来充斥书页。因此,本书中大多数地方的源码引用只是点到即止。很多时候,你会看类似于“...”的省略。或者部分读者会认为在光盘中(而不是书中)阅读源码是件痛苦的事。但是我相信,如果与使用数页甚至十数页的源码来充填内容、增加厚度相比,将这些东西放在光盘中还是更对得起读者的钱包的。两本书 这里提到两本书。提及这两本书的原因,是因为在底层以及原理的分析方面,这两本书当属扛鼎之作。因此本书会在内容上尽量避免与它们的重复。其一是Ray Liscbner著、电力出版出的《Delphi技术手册》。这本书基于Delphi 5,用130页的内容概述了Delphi的基本语言特征,其后的500页则是一个完整的语言参考。基本语言特征部分言简意赅但直指核心,句句发人深省;语言参考部分十分详尽,且附有精彩的示例。 其二是李维先生著、电子工业出版社出的《Inside VCL》。介于《Inside VCL》一本内容浩繁,解析深湛,因此本书剔除了与VCL部分有关的内容(原定计划中是有的),至于OOP在Delphi中的实现以及可能的一些VCL方面的补遗,我会放在其它书中单独去讲。而《Delphi技术手册》一书中对RTL的过程、函数描述详尽,因此本文在这些例程的功能方面,也不多费笔墨。.NET相关问题 本书不讲.NET Framework。在绝大多数读者看来,.NET的出现,意味着Delphi的终结,很多朋友甚至对写这样一本Delphi内核分析的书的必要性提出质疑。然而在我看来,即便是.NET,在内核结构上仍与Win32内核存在千丝万缕的联系,更何况.NET中的非托管代码,还需要用Win32原生应用开发工具来实现。所以Delphi 7.x与Delphi .NET是两条不同的产品线,也有着不同的发展方向。即使是在.NET大行其道的时候,Win32开发以及Win32应用的维护仍然要持续很长一段时间,正如至今还有人在用汇编写驱动程序一样。致谢 我当然得感谢Nico Bendlin(nicode@gmx.net),我从未见过、也没有联系过他,但是如果没有他的MiniDExe,我可能永远也不能开始写这本书。我得感谢那个著名的Delphi大富翁论坛(http://www.delphibbs.com/)以及活跃其中的诸多朋友。我的工作离不开大家的支持和帮助。谢谢。我得感谢CSDN与电子工业出版社的朋友。这包括蒋涛先生、韩磊先生、周筠女士,以及所有关注本书的编写、出版、发行的朋友。我还得感谢我身边的她(Joy Xie)。尽管经过长达数年之久的斗争,她仍然未能迫使我改掉昼伏夜出的作息习惯。但是我得承认,如果没有她,我无法写完这本书(中途死于饥饿或本书终结于我觅食的过程中)。此外,在写这本书的过程中,我们结婚了。感谢给予我支持和帮助的所有朋友。