简介:Absolute Database v7.93 是面向 Delphi 4 至 11 开发者的数据库引擎源代码包,适合研究关系型数据库实现、进行二次开发或定制企业级数据组件的技术人员。压缩包共 756 个文件,约 18.9MB,包含 pas、dcu、dpk、dfm 等 Delphi 源码与组件文件,sql、sqm 脚本,以及 hpp、c、cpp 等跨语言接口和示例资源,另有 res、ico、chm 等辅助文件,覆盖编译、设计与文档环节。已有 446 人学习下载。借助完整源代码,读者可深入理解事务处理、缓存管理、权限加密、备份恢复与多用户并发等机制,并参考 DBManager 等项目文件完成编译验证和功能裁剪,为 Delphi 11 环境下的数据库应用开发提供可复用的工程基础。
1. 从一份标着 D4-11 的源码包说起:装错分支比装不上更难查
老项目要重新打包,我在一台还跑着 Delphi 7 的开发机上翻出 Absolute Database v7.93 的源码包,文件名后缀写着 for D4-11,意思是 Delphi 4 到 11 一套源码通吃。当时想得挺简单:双击包工程、Compile、Install,组件面板出现页签就算装完。结果控件能拖、一编译就报找不到单元,回头翻目录才发现这份源码包内部是按编译器版本分文件的,选错分支不会当场报错,只会在链接阶段翻脸。
这套数据库组件在单文件、零部署、不依赖外部数据库服务的桌面项目里出现频率很高,遗留工程迁移、现场离线部署都爱用它。装不好最典型的后果不是编不过,而是编过了、跑起来偶尔锁库,或者换台机器就打不开。适合两类人看:还在维护 D4 到 D11 老工程的人,以及要给客户机塞一个本地库、又不想让对方装服务端的人。
2. 装机前先对版本:v7.93 的 D4~11 分支划分与文件构成
源码包最容易翻车的地方不在安装动作本身,而在「我该拿哪一份」这一步。Absolute Database v7.93 这个版本号对应的是一套覆盖 Delphi 4 到 Delphi 11 的源码,同一个压缩包里并存着不同年代的包工程和条件编译分支,先对清楚版本,后面几条命令才不会白敲。
2.1 版本对照:D4 到 D11 各阶段的编译差异
下面这张表是我按照实际编译经验整理的,列的是每个阶段真正会卡人的点,不是版本发布说明。表里没有写死的目录名,因为不同打包方式目录结构会有出入,以解压出来的实际内容为准。
| 编译器阶段 | 目标平台 | 包工程形态 | 装的时候最该注意什么 |
|---|---|---|---|
| Delphi 4 / 5 | Win32 | 老式.dpk | 没有命令行初始化脚本,只能在 IDE 里编译 |
| Delphi 6 / 7 | Win32 | .dpk | 包名不能带路径分隔符,目录别放太深 |
| Delphi 2005 ~ 2007 | Win32 | .dpk/.bdsproj | 首次引入工程组,注意别只编了组里的一个 |
| Delphi 2009 / 2010 | Win32 | .dproj | 字符串默认变 Unicode,老代码的PChar会大量告警 |
| Delphi XE ~ XE8 | Win32 / Win64 | .dproj | 从 XE2 起有 64 位目标,指针宽度变了 |
| Delphi 10.x / 11 | Win32 / Win64 | .dproj | 多平台目标并存,别在 Linux64 目标上编 Windows 包 |
表里最需要盯的是两行:2009 那一行和 XE2 那一行。2009 开始Char变成WideChar、String变成UnicodeString,一份 2007 年能编过的源码,在 2009 上会刷出几百条W1057 Implicit string cast;这类告警不影响编译,但如果源码里有地方把PChar硬转成PAnsiChar再交给文件 API,运行时就会出现路径被截断这种玄学问题。XE2 之后有 64 位目标,指针从 4 字节变 8 字节,源码里凡是把指针塞进Integer的写法都会直接报错,报错信息通常是E2010 Incompatible types: 'Integer' and 'Pointer'。
提示:先确认你当前 IDE 的主版本号,再去源码目录里找对应的包文件,不要拿着 D11 的包去 D7 里打开,反过来也一样。
2.2 解压后先做三件事:看目录、列包、数单元
解压到一个纯英文、无空格的路径下,比如D:\sdk\absdb793。带空格的路径在某些老版本命令行编译时会被截断,C:\Program Files\...是重灾区。解压完别急着双击,先开一个命令行窗口做三件事。
# 1) 进到解压根目录 cd /d D:\sdk\absdb793 # 2) 看一级目录,判断源码包是平铺结构还是按版本分了子目录 dir /b # 3) 列出所有包工程文件,这是后面要编译的入口 dir /b /s *.dpk dir /b /s *.dproj三条命令的输出决定后面怎么走。如果dir /b直接吐出一堆.pas,说明是平铺结构,老版本源码包常见这种排布,好处是单元都在一层、搜索路径只用加一个;如果吐出的是Source、Packages、Lib、Demo这类目录名,那就是分层结构,搜索路径至少要加两层。第二、三条命令列出来的包文件是这次安装真正的入口,命名上通常能分出运行期包和设计期包,前者只含逻辑、后者只含注册代码。拿到实际文件名之后,把下面几章命令里的占位名替换掉。
顺便看一眼单元数量,能提前判断编译耗时:
# 统计 .pas 数量,Windows 下用 find 的计数模式 dir /b /s *.pas | find /c /v ""2.3 安装前的四个前置条件
第一,目标 IDE 必须全部关掉。设计期包编译出来是.bpl,安装时 IDE 会把它加载进进程,如果 IDE 已经开着且加载过旧版本,新的.bpl覆盖不掉,会报「文件被另一个程序使用」。我一般是先关 IDE,再开命令行,顺序反了就得重来。
第二,源码目录的只读属性去掉。从压缩包里解出来的文件经常带只读标记,编译器写.dcu时会在同目录生成中间文件,只读状态下直接失败,报错还不明显。
# 递归去掉只读属性 attrib -R D:\sdk\absdb793\*.* /S/S表示连同子目录一起处理,这一步在从共享盘或只读镜像里拷出来的源码包上尤其必要。
第三,确认磁盘够用。一个带几十个单元的 Delphi 包,Debug 和 Release 两套.dcu加起来几百兆很常见,C 盘紧张的时候把中间文件输出目录改到别的盘,别等到E2045那种莫名其妙的编译失败才回头查。
第四,如果你要在 D4/D5/D6/D7 这种老 IDE 上装,命令行编译这条路基本走不通,因为这些版本没有后来那套环境初始化脚本,得直接在 IDE 里打开包工程、按 Compile 再按 Install。后面第 3 章讲的命令行方式只适用于有rsvars.bat的版本,老版本看 3.3 节的手工安装流程。
3. 编译与安装:从环境变量到组件面板出现页签
这一章是整套安装动作的核心。顺序只有一个:先编运行期包,再编设计期包,最后在 IDE 里 Install。顺序反了会得到一句Cannot find package,看着像缺文件,其实是设计期包在编译时找不到它requires的那个运行期包。
3.1 用 rsvars.bat 把命令行编译环境拉起来
Delphi 从 XE2 之后自带一个环境初始化脚本,通常在 IDE 安装目录的bin下,叫rsvars.bat。它的作用是设置BDS、FrameworkDir、FrameworkVersion以及把编译器加进PATH,不跑这个脚本直接敲msbuild会报找不到 Delphi 的目标文件。
# 路径里的 22.0 对应 Delphi 11,你装的是哪个版本就改成对应数字 call "C:\Delphi\22.0\bin\rsvars.bat" # 验证环境是否生效,能打印出版本号说明对了 echo %BDS% dcc32 --versioncall不能省。在批处理里直接执行另一个 bat 会把当前脚本的控制权交出去,后面的命令就不执行了;在交互式命令行里其实也一样,call是最稳的写法。%BDS%打印出来应该是一个 Studio 目录路径,如果是空的,说明rsvars.bat的路径写错了,或者你装的版本根本没有这个脚本。
一个常见误区是把rsvars.bat的内容抄进系统环境变量里一劳永逸。不建议这么干,因为同一台机器上经常并存两个 Delphi 版本,全局写死会导致打开 A 版本的 IDE 却用 B 版本的编译器,产出的.bpl版本不匹配,装进去就崩。
3.2 先编运行期包:只含逻辑,不含注册
运行期包是给最终 exe 用的,特征是不含Register过程。编译命令用msbuild走工程文件,比dcc32更省事,因为工程文件里已经写好了搜索路径和条件定义。
# /t:Build 是完整重建,比 /t:Rebuild 温和,能保留已有的 .dcu 缓存 msbuild D:\sdk\absdb793\Packages\ABSRuntime.dproj ^ /t:Build ^ /p:Config=Release ^ /p:Platform=Win32 ^ /p:DCC_UnitSearchPath="D:\sdk\absdb793\Source;$(DCC_UnitSearchPath)"^是 Windows 命令行的续行符,一行写完可以去掉。四个参数里最容易被忽略的是最后一个:DCC_UnitSearchPath覆盖的是工程级的搜索路径,如果源码包是分层结构,这里要写全,Source和Source\Common这种子目录都要用分号串起来,漏一层就会在编译到某个单元时报F1026 File not found。Config=Release是我个人的习惯,Release 模式下编译器的警告级别和优化都会打开,能提前暴露一些 Debug 下被吞掉的问题;如果你只是想先装上跑通,用 Debug 也行,但发布前一定要用 Release 重编一次。
编译成功的标志是输出目录里出现.bpl和一堆.dcp。.dcp是编译期需要的符号文件,.bpl是运行期需要的包体,两个都得留着,后者在下一步安装设计期包的时候会被用到。
3.3 编设计期包并 Install 到 IDE
设计期包requires上一步编出来的运行期包,编译命令几乎一样,只改工程名。
msbuild D:\sdk\absdb793\Packages\ABSDesign.dproj ^ /t:Build ^ /p:Config=Release ^ /p:Platform=Win32编完之后,把产出目录里那个设计期.bpl加载进 IDE。有两种做法:命令行直接调 IDE 的安装入口,或者打开 IDE 走菜单。命令行方式适合批量部署开发机:
# 加载设计期包到 IDE,路径改成你实际的 bpl 位置 "C:\Delphi\22.0\bin\bds.exe" -pDelphi -zRescan-pDelphi指定加载的是 Delphi 个人版以上的配置档,-zRescan让 IDE 重新扫描组件注册表。这一步执行完,打开 IDE 新建一个 VCL 工程,组件面板上应该多出一个页签,里面能看到数据库组件、数据集组件和查询组件。
对没有rsvars.bat的老版本,只能手工来:IDE 里File > Open打开设计期包工程,右键工程节点选Compile,编译通过后右键选Install。Install 这个动作实际做两件事,先检查requires里的运行期包能不能找到,再把包里的Register过程跑一遍。如果 Install 报「找不到 xxx.bpl」,说明上一步的运行期包没有被 IDE 认识,去 3.4 节加搜索路径。
3.4 搜索路径要加在三个地方,各有各的用处
Delphi 的单元搜索路径分三个作用域,很多人只加了一个,结果换个工程又报找不到单元。
| 作用域 | 配置位置 | 生效范围 | 典型用途 |
|---|---|---|---|
| 全局库路径 | IDE 的 Tools > Options > Language > Delphi > Library | 本机所有工程 | 装第三方组件后一次性配好 |
| 工程搜索路径 | Project > Options > Delphi Compiler > Search path | 当前工程 | 工程要发给别人时随工程走 |
| 命令行参数 | -U或DCC_UnitSearchPath | 当次编译 | CI 或脚本批量编译 |
全局库路径是装组件时的主战场,把源码目录和产出.dcp目录都加进去,注意是「加」不是「替换」,Delphi 自带的$(BDSLIB)\$(Platform)\release那一串必须留着,删了连System单元都找不到。工程搜索路径适合那些不希望污染全局的临时验证;命令行参数则是打包机上的首选,因为它不依赖任何 IDE 配置,换台机器照样能编。
加完路径之后一定要重启 IDE。有些版本的库路径是启动时读进内存的,不重启就是不生效,改了没反应会让人怀疑自己加错了地方。这个坑我踩过不止一次。
4. 连接参数与工程配置:让一个最小工程真正读写 .abs 文件
组件面板出现了页签,只证明包装上了,离「能跑」还差配置。这一章用一个最小工程把数据库文件创建、建表、写入、读出整条链路走一遍,顺便把几个必须提前定的工程选项敲死。
4.1 数据库组件的关键属性
Absolute Database 是嵌入式引擎,数据全部落在一个文件里,没有服务端进程,所以连接参数比 C/S 数据库少得多,但少不等于可以随便填。
| 属性 | 作用 | 建议填法 | 容易踩的地方 |
|---|---|---|---|
DatabaseName | 逻辑名,供数据集组件引用 | 固定一个短名,如MainDB | 写成文件路径不会报错,但换目录就全废 |
FileName | 实际数据库文件路径 | 相对 exe 目录,运行时拼绝对路径 | 写死C:\...到客户机上必然找不到 |
Password | 打开文件的口令 | 空串表示不加密 | 有口令时改口令要重新建库,不能原地改 |
ReadOnly | 只读打开 | 查询类工具设为True | 只读打开时做 DDL 会直接抛异常 |
InMemory | 纯内存库,进程退出即丢失 | 单元测试、临时计算 | 误开会让数据「莫名消失」 |
DatabaseName这个设计是历史遗留,它的存在意义是让多个数据集组件共用一个连接而不必各自持有文件路径。实际项目里我一般把它设成和工程名一致的短字符串,然后在数据模块的OnCreate里根据 exe 位置拼FileName,这样打包到任何目录都能跑。
4.2 最小可跑例程:建库、建表、插一条、读回来
下面这段代码是自检用的,可以单独做成一个控制台工程,也可以贴在数据模块的初始化方法里。单元名和类名以源码目录中实际文件名和声明为准,不同分支可能有细微差异。
program AbsSelfTest; {$APPTYPE CONSOLE} uses SysUtils, ABSMain, // 数据库组件所在单元,以实际文件名去掉扩展名为准 ABSTable; // 数据集组件所在单元 var DB: TABSDatabase; Tbl: TABSTable; begin DB := TABSDatabase.Create(nil); Tbl := TABSTable.Create(nil); try // 逻辑名随意,但后续数据集必须和它一致 DB.DatabaseName := 'SelfTest'; // 关键:数据库文件放到 exe 同级目录,避免写死绝对路径 DB.FileName := ExtractFilePath(ParamStr(0)) + 'selftest.abs'; DB.Password := ''; // 空串表示不设口令 DB.ReadOnly := False; DB.Open; // 文件不存在时会自动新建 Tbl.DatabaseName := DB.DatabaseName; Tbl.TableName := 'SELFTEST'; // 字段定义必须在 CreateTable 之前完成,建完再改要重建表 Tbl.FieldDefs.Clear; Tbl.FieldDefs.Add('ID', ftAutoInc, 0, False); Tbl.FieldDefs.Add('TAG', ftString, 40, False); Tbl.CreateTable; Tbl.Open; Tbl.Insert; Tbl.FieldByName('TAG').AsString := 'install-ok'; Tbl.Post; Writeln('记录数 = ', Tbl.RecordCount); // 期望输出 1 finally Tbl.Free; DB.Free; end; end.逐段说明。ExtractFilePath(ParamStr(0))取的是当前可执行文件所在目录,这是嵌入式数据库最稳的定位方式,比GetCurrentDir好,因为后者会被快捷方式的「起始位置」影响,用户在别处双击就换目录了。DB.Open在文件不存在时自动创建,这一点省掉了单独的「建库」API 调用。FieldDefs的Add后面两个参数分别是大小和必填标记,ftAutoInc和ftString之外的类型照抄这个形式即可。CreateTable是 DDL,只读连接下会抛异常,所以ReadOnly必须在此之前设好。
如果这段跑通,说明编译链路、搜索路径、文件权限三件事都没问题。跑不通的话,对照第 5 章逐条查。
4.3 发布时的编译选项:别把 bpl 带进客户机
安装阶段用运行期包是省事的,但发布阶段要不要继续用包,是另一个决定。用包的好处是多个程序共享一份.bpl,体积小;坏处是客户端必须带齐所有.bpl且版本严格一致,缺一个就是启动即报错,错误信息还只给个模块名。
我一般的做法是发布时把源码直接编进 exe,工程选项里关掉这两个开关:
Project > Options > Delphi Compiler Link with runtime packages = False Link with dynamic RTL = False关掉之后 exe 会变大,通常从几百 KB 到两三 MB,换来的是一个文件拷过去就能跑。这一步必须在 Release 配置下验证,Debug 配置里的这两个开关和 Release 是分开存的,只在 Debug 里改完就打包,是另一个常见翻车点。
4.4 32 位和 64 位目标的差异
v7.93 这份源码覆盖到 D11,意味着 64 位目标是要考虑的。在 32 位下编过的包,切到 Win64 重编时最常见的报错是E2010 Incompatible types: 'Integer' and 'Pointer'和E2064 Left side cannot be assigned to。这类都是把指针当整数用的老写法,改法有两条:换成NativeInt/NativeUInt,或者干脆在工程里只保留 Win32 目标。
如果客户环境没有 64 位强需求,我倾向只保留 Win32,理由很实际:单文件数据库在 32 位进程里能用的地址空间通常是 2GB 上下,够绝大多数桌面场景用;强行上 64 位要么改源码、要么等着在老单元上反复翻车。真要上 64 位,就得把包里所有涉及指针转换的地方逐个过一遍,这部分工作量不小。
5. 常见问题排查:五个把人卡住的现场
安装过程里的报错大多不指向真正的原因,下面五条是我在不同机器上真实遇到过、并且能在十分钟内定位的。每条按现象、原因、解决三段写,照着对号入座就行。
5.1 组件面板有页签,但拖上去就报找不到单元
现象:IDE 里能看到组件页签,拖一个数据库组件到窗体上,编译时提示F1026 File not found: 'ABSMain.dcu'。
原因:设计期包装进去了,但运行期包没有被工程识别。设计期包只负责注册,真正提供单元的是运行期包,工程编译时找的是.dcp和源码路径,不是.bpl。
解决:回到 3.4 节,把产出.dcp的目录和源码目录一起加进全局库路径,重启 IDE 再编。如果还是找不到,用dir /s ABSMain.*确认这个单元到底在哪个子目录里,很可能搜索路径少写了一层。
5.2 Install 时报 Cannot find package
现象:右键设计期包选 Install,弹出对话框说找不到某个包。
原因:这个包在requires里引用了运行期包,IDE 按包名去找,找到的是包文件里的标识名而不是文件名,两者不一致就找不到。
解决:先把运行期包 Install 一次(哪怕它没有注册代码也可以装),再回来装设计期包。装完可以去注册表里确认标识名和文件名是否对应,不对的话改包工程的{$IMPLICITBUILD OFF}附近的标识声明,重新编译。
5.3 开发机跑得好,客户机启动就崩
现象:本机双击 exe 一切正常,拷到客户机上启动直接弹「找不到模块」或者程序秒退。
原因:工程还在用运行时包,bpl没跟着走,或者跟着走了但版本和 exe 编的时候不一致。
解决:按 4.3 节关掉两个开关,Release 配置下重编,只拷 exe 和数据库文件。如果业务上必须用包,那就得把bpl和 exe 一起放进同一个目录,并且保证编 exe 和编bpl用的是同一个 IDE 版本,跨版本混用一定会崩。
5.4 数据库文件打不开,提示拒绝访问
现象:DB.Open抛异常,提示无法访问文件,但文件明明存在。
原因:三种可能。一是路径里含中文,某些老版本的文件 API 在非 Unicode 编译下会截断;二是程序装在Program Files下,普通用户没有写权限,而 UAC 会把写操作重定向到虚拟存储,导致你看到文件生成了、下次又找不到;三是文件被另一个进程以独占方式打开着。
解决:先看路径,挪到D:\下的纯英文目录再试。再看权限,别把可写数据库放在Program Files,这是新手最容易踩的。最后确认没有第二个实例在跑,任务管理器里翻一遍同名进程。
5.5 命令行编译报 MSB8036 找不到 Windows SDK
现象:msbuild一上来就报找不到某个 Windows SDK 版本。
原因:工程文件是从别的机器拷过来的,里面记着对方的 SDK 版本号,本机没装这个版本。
解决:不要手工去改工程文件里的 SDK 版本,用 IDE 打开这个工程一次,在Project > Options > Delphi Compiler里让 IDE 重新选一遍目标平台,保存之后工程文件里的版本号会被自动改成本机可用的。这一步改完再回到命令行编,就不会再报。
6. 装完怎么验证跑通:十分钟自检清单
组件装上了、能编译,和「这套环境真的可用」之间还有一段距离。我给自己定了一套十分钟能走完的验收动作,覆盖编译、链接、运行、发布四个环节,每次换机器装完都过一遍,省得后面在客户现场救火。
| 检查项 | 怎么验 | 通过标准 |
|---|---|---|
| 运行期包 | 看产出目录 | 有.bpl和.dcp,时间戳是最新的 |
| 设计期包 | 打开 IDE 看组件面板 | 页签存在,能拖出数据库和数据集组件 |
| 单元可见性 | 新建工程uses一下 | 编译通过,无F1026 |
| 建库读回 | 跑 4.2 节的自检程序 | RecordCount输出 1 |
| 发布形态 | Release 重编后拷到干净目录 | 双击能启动,不依赖任何bpl |
表格里前四项十分钟内能走完,最后一项最好找一台没装过 Delphi 的机器试。没有第二台机器的话,把 exe 拷到D:\tmp这种干净目录、把 IDE 关掉再双击,也能测出八成问题。
自检程序跑完之后,别急着删selftest.abs,用它再做一次只读验证,能提前发现权限配置的坑:
// 只读打开同一个文件,验证权限与锁定行为 DB.ReadOnly := True; DB.Open; // 应该成功 try Tbl.CreateTable; // 只读下做 DDL,应该抛异常 except on E: Exception do Writeln('预期内的失败:', E.ClassName); end;这段的意义在于确认两件事:一是只读模式真的生效了,二是异常类型和你后续代码里的except分支对得上。很多人只测 happy path,等到线上只读查询突然抛异常才发现自己的异常捕获写错了类。
还有一个容易被忽略的点:编译出来的 exe 用Dependency Walker之类的工具看一眼依赖列表,如果里面出现rtl*.bpl、vcl*.bpl这种条目,说明运行时包没关干净,工程选项里某个配置还留着旧设置。这个检查比在客户机上试错便宜得多。
从那以后我每装一次第三方组件,都强制走一遍「命令行编译 + 自检程序 + 干净目录启动」三件套,不图快。装环境这件事,快和省事往往是同一件事的两面,前面省下的十分钟,后面大概率要用一个下午来还。希望帮到你。
本文还有配套的精品资源,点击获取