简介:面向 Delphi 开发者的 Excel 文件读写组件包,版本为 XLSReadWriteII v6.0.0.47,兼容 D7 至 D10.3 Rio,适合需要在程序中直接生成、解析和修改 Excel 文档的桌面与数据库项目。它无需本机安装 Excel,内置面向单元格值优化的内存管理,并提供 Direct Read/Write 模式处理超大文件;引擎强调不破坏原文件结构,完整源代码一并提供。压缩包共 596 个文件,约 2.59MB,以 pas 源码、html 文档、dfm 窗体、dpr/dproj 工程及 obj、png 示例资源为主,便于查阅组件实现、示例工程和帮助资料。已有 302 人学习,适合希望减少 Office 依赖、研究 Delphi Excel 组件封装与文件兼容性处理的开发者参考。
1. 一份包同时支持 D7 和 D10.3 Rio:先别急着双击 bpl
上周有人把一份标着 for D7 / D10.3 Rio 的 Excel 读写组件包发过来,说装是装上了,组件面板也确实多了页签,可工程一编译就报找不到单元;把路径补全之后,运行期又直接 AV。这套包最容易让人误判的地方就在名字上:同一个压缩包要同时支持 Delphi 7 和 Delphi 10.3 Rio 这两个相差十几年的 IDE,里面的 DCU、包工程、搜索路径全部按版本分开存放,拿错一份不会立刻报错,而是在你拖完控件、写了几十行代码、准备交付的前一晚突然出问题。
后面按我实际装机的顺序走一遍:先把版本和目录对齐,再分别装 D7 和 Rio 两侧,然后给出装崩了怎么恢复的排查清单,最后用一个最小的 xlsx 读写回环确认真的跑通。适合手上同时养着老 D7 项目和新 Rio 项目的人,也适合第一次给 Delphi 装第三方 Excel 组件的人。
2. 装之前先对齐:D7 和 Rio 的包结构差在哪
拿到这种“一包多版本”的分发,第一件事是先把目录结构和文件类型看懂,不要急着打开 IDE。装错版本引起的症状往往延迟出现,早十分钟看清楚目录能省掉后面两小时的排查。
2.1 源码、DCU、设计期包,三样东西别混着用
解开压缩包,通常能看到这几类内容:pas 源码目录、按 IDE 版本分好的 DCU 目录、若干 .dpk 或 .dproj 包工程、Demo 与帮助文档。这三类东西对应三种完全不同的用法,选错一种就会出现“明明装上了却用不了”的情况。
只把源码目录加进库路径,是最稳的做法:每次编译都从源码重新编一遍,速度慢一点,但不受编译器版本限制,D7 和 Rio 都能用同一份源码,出了问题也好打断点跟进去。只把 DCU 目录加进库路径,编译快,可 DCU 是编译器的中间产物,和编译器版本强绑定,D7 编出来的 DCU 拿到 Rio 下必然报格式错误,Rio 10.2 编的 DCU 升到 10.3 也可能要重编。
装设计期包则是为了组件面板:编出 .bpl 并 Install 之后,组件页签才会出现,才能拖控件、在 Object Inspector 里改属性。运行期其实不依赖这个安装动作,工程里 uses 对应单元、库路径对得上就能跑。理解这一点很关键,后面排查时你会反复用到“设计期”和“运行期”这两条线是分开的。
| 形态 | 放哪 | 能否跨 IDE 版本 | 适用场景 |
|---|---|---|---|
| pas 源码 | 加入 Library path | 能,D7 与 Rio 共用同一份 | 长期维护、需要调试跟进源码 |
| DCU | 加入对应平台的 Library path | 不能,编译器版本强绑定 | 只想快速编译、不关心内部实现 |
| 设计期包(bpl) | IDE 内 Install 或注册表注册 | 不能,且只有 Win32 能装 | 需要组件面板与属性编辑器 |
2.2 目录和 IDE 的对应关系
分发习惯各家不同,但大致逃不出“按 IDE 大版本分目录、再按目标平台分子目录”这个套路。判断依据不要看目录名写得多漂亮,看里面的文件:DCU 目录下应该有一堆 .dcu,包工程目录下应该有 .dpk 或 .dproj,源码目录下应该是 .pas 和 .inc。
| 目录形态 | 对应 IDE | 目标平台 | 说明 |
|---|---|---|---|
| 名称含 D7 的目录 | Delphi 7 | Win32 | D7 只有 32 位,DCU 为 ANSI 版本 |
| 名称含 D10.3 且带 Win32 子目录 | 10.3 Rio | Win32 | 设计期包只能取这里的 |
| 名称含 D10.3 且带 Win64 子目录 | 10.3 Rio | Win64 | 与 Win32 那份不通用,必须单独一份 |
| Source 或 Src 目录 | 通用 | 通用 | 源码,两个 IDE 都能编 |
提示:目录名只是线索,最终以包内 Demo 工程引用的路径为准。打开任意一个 Demo 的工程,看它的搜索路径指向哪个目录,那个目录就是这套包认可的入口。
2.3 两个 IDE 的“库路径”根本不是一回事
D7 的库路径在 Tools → Environment Options → Library 里,全局一份,分号分隔,没有平台概念,改一次对所有工程生效。Rio 完全变了:路径在 Tools → Options → Language → Delphi → Library 里,按平台分栏,Win32 一栏、Win64 一栏、Android 一栏,彼此独立;而且每个平台下还分成 Library path 和 Browsing path 两项。
Library path 真正参与编译,配错了就报找不到单元。Browsing path 只服务于代码导航,配错了不影响编译,但你 Ctrl+Click 跳不进源码,很多人一看跳不进去就以为没装好,回头把已经对的 Library path 改乱了。Rio 这套按平台分栏的设计,正是后面“Win32 好好的,一改 64 位就全崩”的根源。
顺带说一句命名空间。Rio 的工程选项里有一项 Unit scope names,默认带了一串 System、Vcl、Data 之类的前缀。老包里如果有单元用短名引用 RTL 单元,或者包里自己有个以 System 开头命名的单元,命名空间设置不对就会报找不到,但 D7 下同样的代码一点问题都没有。
2.4 同一份 Demo 在两边表现不一样的原因
D7 的 string 就是 AnsiString,Rio 的 string 是 UnicodeString。往 Excel 里写中文,这个差异会直接变成可见的结果:D7 下如果中间隔了一层 UTF-8 编码转换,写进去就是问号或者乱码;Rio 下因为 string 本身就是 Unicode,同样的代码反而正常。
// Delphi 7:把中文安全地送进单元格 var S: WideString; begin S := '订单号'; // 由 RTL 按系统代码页做 AnsiString -> WideString XLS.Sheets[0].AsString[0, 0] := S; // 如果 S 来自接口或网页的 UTF-8 字节串,先 UTF8Decode 再赋值 end;这段没什么玄学,关键在类型:这个组件面向单元格的文本接口按宽字符处理,D7 下传 AnsiString 时会发生隐式转换,代码页不对就丢字。我的习惯是 D7 侧一律显式声明 WideString 变量,宁可多写一行也别赌默认行为。Rio 侧不用管,但要注意别把老的 AnsiString 变量硬塞进去,那样等于主动把 Uni code 降级回 ANSI。
3. Delphi 7 侧:从 dpk 编到组件面板出现
D7 这一侧的安装反而比 Rio 简单,因为它没有平台概念,插件数量也少。但 D7 的 IDE 对路径的容忍度很低,路径里带空格、带中文、带括号,都可能在编包时给你一个莫名其妙的错误。
3.1 动手前先留一个恢复点
D7 的已安装包列表在注册表里,装崩了以后靠“再装一遍”往往是修不回来的,因为重复项会一直堆着。装之前花十秒导出一份,后面遇到问题能立刻恢复。
reg export "HKCU\Software\Borland\Delphi\7.0" d7_registry_backup.reg这条命令把 D7 的用户级配置整棵导出,包含 Known Packages 列表。恢复时双击导入即可,导入前记得先关掉 IDE,否则关闭 IDE 时代码会再把当前内存里的包列表写回注册表,等于白导。同时把 Component → Install Packages 窗口里的已装列表截图留档,用来对照哪些是原装的、哪些是你加的。
3.2 先把搜索路径配对,再谈装包
顺序不能反。先配路径,再编包,最后 Install。路径配错的情况下直接去编 dpk,编译器会在第一步就报找不到依赖单元,你还以为是包不完整。
在 Tools → Environment Options → Library 的 Library Path 末尾追加两行,源码在前、DCU 在后:
D:\Comp\XLSReadWriteII\Source D:\Comp\XLSReadWriteII\D7源码放前面是有意的:只要源码存在,编译器优先用源码重编,DCU 只作为兜底,这样即使 DCU 目录和你的 D7 小版本对不上也不会直接崩。路径不要带空格,尤其不要放在C:\Program Files这类目录下,老 IDE 在处理 dpk 输出路径时对空格的处理一直不太稳。加完点 OK 之后重启一次 IDE,让路径真正生效。
3.3 编译并安装设计期包
用 File → Open 打开包目录里的 .dpk,先别急着点 Install。Project → Build 走一遍,看编译输出窗口最后一行是不是成功。编译通过后再点 Install,IDE 会把 bpl 装进 Known Packages 并把组件注册到面板。
如果分发里根本没有 dpk,只有一堆 DCU,那就跳过装包这一步:把 DCU 目录加进 Library path,工程里直接 uses 对应单元,动态创建对象即可,只是组件面板上不会出现页签。这个分支很多人不知道,白白找了半天“为什么没有 dpk”。
安装成功后,在 Component → Install Packages 里应该能看到新增项,它的路径指向你刚编出来的 bpl 所在目录。如果路径指向的是别的目录,说明你编的不是当前这份包,或者之前装过旧版本被复用了。
3.4 用 dcc32 在命令行复现一次
这一步的目的不是替代 IDE,而是定位问题边界:命令行能过、IDE 不能过,问题一定在 IDE 的库路径或包引用上,跟代码没关系。参数看着多,其实就四个作用:告诉编译器去哪找单元、把中间产物放哪、把 bpl 放哪、把 dcp 放哪。
dcc32 -B ^ -U"Source;D7" ^ -I"Source" ^ -NU"build\dcu" ^ -LE"build\bpl" ^ -LN"build\dcp" ^ XLSReadWriteII_D7.dpk-B 强制全部重编,避免旧 DCU 混进来;-U 是单元搜索路径,分号分隔,这里同时给了源码和 DCU 目录;-I 是 include 文件路径,包里如果有 .inc 就得给;-NU、-LE、-LN 分别指定 DCU、bpl、dcp 的输出目录,三个一定要分开到不同文件夹,混在一个目录里下次清理时很容易误删。包名以包内实际的 dpk 文件名为准。
命令跑完看输出目录:有 bpl 有 dcp 才算真编出来了。只有 dcu 没有 bpl,通常是 dpk 里requires的运行时包名字写错,或者你手上这份 dpk 是运行期包而不是设计期包。
3.5 装完先写一张空表,别急着上业务代码
组件面板出现页签不等于能用。我一般会立刻新建一个空白工程,写十行代码存一个文件出来,这一步能把“路径对但版本不对”的问题当场暴露。
var XLS: TXLSReadWriteII; begin XLS := TXLSReadWriteII.Create(nil); // 动态创建,绕开组件面板,验证的是单元本身 try XLS.Clear; // 清掉实例里的默认内容,从空表开始 XLS.Sheets.Count := 1; XLS.Sheets[0].AsString[0, 0] := 'Excel 写入测试'; XLS.WriteToFile('d:\tmp\d7_smoke.xlsx'); // 扩展名决定写出 xls 还是 xlsx finally XLS.Free; end; end;两个细节值得记住。第一,索引是“先列后行、从 0 开始”,AsString[0, 0]是 A1 单元格,跟 StringGrid 的 (Col, Row) 一致,跟 Excel 的 A1 表示法正好反过来,写循环时最容易在这里错一位。第二,WriteToFile的格式由扩展名推导,想要 xlsx 就给 .xlsx,想要老的二进制格式就给 .xls,别指望用同一个文件名加参数切换。
4. Delphi 10.3 Rio 侧:32 位设计期、64 位运行期,分开做
Rio 这一侧复杂度全在“位”上。同一个包工程要编出两份东西:一份 32 位的设计期包给 IDE 用,一份 64 位的运行期包给最终 exe 用,而且这两份不能互相顶替。
4.1 Rio 的 IDE 本身是 32 位
这一点必须先明确,否则后面所有现象都解释不通。Rio 的 bds.exe 是 32 位进程,它只能加载 32 位的设计期包。如果你把 Win64 编出来的 bpl 塞进 Known Packages,IDE 启动时会加载失败,运气好只是组件页签消失,运气不好直接弹一个加载错误然后整个 IDE 的包列表错乱。
所以规则很简单:设计期包只编 Win32;Win64 那份只编运行期包,不 Install、不注册,只把 DCU 路径加进 Win64 的 Library path。Rio 注册表里对应的位置是HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Known Packages,20.0 就是 10.3 这一代的版本号,装别的版本时这个数字要跟着换。
4.2 用 rsvars 加 msbuild 编 Win32 设计期包
不要在 IDE 里点,用命令行编,因为命令行能明确指定平台,IDE 里点来点去很容易在平台下拉框上选错。rsvars.bat 的作用是把 dcc32、msbuild、BDS 这些环境变量一次性设好,不跑它 msbuild 会找不到编译器。
call "C:\Program Files (x86)\Embarcadero\Studio\20.0\bin\rsvars.bat" msbuild XLSReadWriteII.dproj /t:Build /p:Config=Release /p:Platform=Win32 ^ /p:DCC_UnitSearchPath="D:\Comp\XLSReadWriteII\Source"第一行的路径按你实际的 Studio 版本目录调整,20.0 对应 Rio。/p:Platform=Win32大小写敏感,写成 win32 可能被忽略然后回落到默认平台。DCC_UnitSearchPath是临时补的单元搜索路径,等价于在 IDE 里改库路径,好处是不污染全局配置,编完就没了。默认输出目录是C:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl,编完去那里找 .bpl。
拿到 bpl 之后回到 IDE,Component → Install Packages → Add,选中它。装完检查 Known Packages 里那条记录的路径,如果指向的是公共 Bpl 目录说明正常,指向你的源码目录说明哪里配歪了。
4.3 Win64 只编运行期,一个字都别 Install
msbuild XLSReadWriteII.dproj /t:Build /p:Config=Release /p:Platform=Win64 ^ /p:DCC_UnitSearchPath="D:\Comp\XLSReadWriteII\Source"同样的命令,只把平台换成 Win64。编出来的 bpl 用于运行期动态包方式,更常见的用法是干脆不用运行期包,直接让编译器链接 DCU,此时你只需要把 Win64 的 DCU 输出目录加进 Library path 的 Win64 那一栏。两种方式都行,选一种并坚持,别一半工程用包一半用 DCU,出问题很难定位。
注意:Win32 和 Win64 的 DCU 绝对不能混用。同一个目录下同时存在两种位数的 DCU 时,编译器不一定会报错,而是悄悄链接到错的那份,运行期表现为地址异常或类找不到。
4.4 库路径要按平台一栏一栏配
这是 Rio 侧最高频的一个问题。Win32 下配好了路径,编译运行一切正常,改到 Win64 编译立刻报找不到单元,因为 Library 对话框里 Win64 那一栏是空的。Rio 的每栏路径是独立的,你加一次只对一个平台生效。
我的做法是配完 Win32 之后立刻切到 Win64,把同样的两条路径再贴一遍,然后再切回来确认两边都在。切平台的下拉框在 Tools → Options 对话框的右上方,改完记得点 Save 而不是直接关窗口。Browsing path 也照同样方式补一份,不然 64 位工程里跳源码会失效,虽然不影响编译,但调试时很难受。
4.5 IDE 装不上时的兜底:直接注册设计期包
极少数情况下 Install Packages 对话框点 Add 没反应,或者加进去重启 IDE 后又消失了。这时候可以绕过界面直接写注册表,前提是你已经按 3.1 的思路备份过。
reg add "HKCU\Software\Embarcadero\BDS\20.0\Known Packages" ^ /v "C:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl\XLSReadWriteII_D10_3.bpl" ^ /t REG_SZ /d "XLSReadWriteII" /f值名必须是 bpl 的完整路径,这是 Known Packages 的约定;数据部分是显示名,随便写但建议写清楚,方便日后在列表里认出来。路径写错的话 IDE 启动时要么静默忽略,要么弹一个加载失败提示。改完一定要完全退出并重启 IDE,只关工程窗口不生效。
5. 排查清单:五个现象对应的真实原因
装这类包踩的坑高度重复,下面五条按我遇到频率从高到低排。每条都按“看到什么、为什么、怎么修”的顺序写,照着对号入座就行。
5.1 组件面板有页签,一编译就报找不到单元
现象:组件页签出来了,控件能拖,但一按 F9 就报Unit 'xxx' not found。原因几乎总是库路径没配或配错了平台——设计期包安装成功只说明 bpl 加载成功,它不会自动帮你把源码或 DCU 目录加进编译期搜索路径。解决:打开 Library 对话框,确认当前平台那一栏里有源码目录;用 Rio 时特别检查是不是只配了 Win32 而当前在编 Win64。
5.2 打开工程弹出 “contains unit which is also contained in package”
现象:IDE 一启动或一打开工程就弹包冲突提示。原因是同一个库装了两份,比如旧版本的 bpl 没卸干净又装了新的,或者把 D7 那份混进了 Rio 的 Known Packages。解决:进 Install Packages 列表,把指向旧目录的条目 Remove 掉,再检查 Known Packages 注册表项里有没有残留的旧路径,两处都清干净后重启 IDE。
5.3 设计期一切正常,运行期直接 AV
现象:能在设计器里拖控件、能在属性面板改属性,一运行就 AV 或 Invalid pointer operation。原因是编译期用的 DCU 和运行期加载的 bpl 不是同一批编出来的,常见于中途换过目录、改过 Config。解决:把工程的 DCU 输出目录整个清空,用 -B 或 IDE 的 Build All 强制全量重编;如果用了运行期包,确认 exe 旁边的 bpl 与编译时那份版本一致。
5.4 D7 写进去的中文变成问号
现象:Rio 下同样的代码写入正常,D7 下单元格里全是问号或方块。原因是 AnsiString 与 Unicode 的转换在中间某一步丢了代码页。解决:所有参与单元格赋值的字符串变量声明成 WideString,接口返回的 UTF-8 字节串先 UTF8Decode 再赋值,不要在中间反复做 AnsiString 与 string 的互转。
5.5 xlsx 生成成功,Excel 打开提示需要修复
现象:文件确实写出来了,大小也正常,用 Excel 打开弹“发现不可读取的内容”。原因通常是扩展名与实际格式不符,比如拿 xls 的写法硬写了个 .xlsx 文件名,或者文件在写入过程中被另一个进程占用、只写了一半。解决:删掉重写一次,写入前确认目标文件没有被 Excel 或预览程序占用;然后换一个纯 xlsx 的扩展名做对照测试,能区分出是格式问题还是占用问题。
6. 装完怎么验证:一个最小读写回环
验证这一步别偷懒,也别拿业务代码当试验田。我习惯的做法是写一个十行的回环:建表、写值、存盘、读回、比对,四步走完,装没装好当场就有结论。关键点是写完必须读回来,只写不读只能证明 API 不崩,证明不了数据真的落进去了。
先准备一个最小工程,动态创建组件,绕开组件面板,这样验证的是库本身而不是安装动作:
var XLS: TXLSReadWriteII; S: WideString; begin XLS := TXLSReadWriteII.Create(nil); try XLS.Clear; XLS.Sheets.Count := 1; S := '中文测试'; XLS.Sheets[0].AsString[0, 0] := S; // A1:字符串 XLS.Sheets[0].AsFloat[1, 0] := 1234.56; // B1:数值 XLS.WriteToFile('d:\tmp\roundtrip.xlsx'); XLS.Clear; XLS.ReadFromFile('d:\tmp\roundtrip.xlsx'); Assert(XLS.Sheets[0].AsString[0, 0] = S); // 中文是否原样回来 Assert(Abs(XLS.Sheets[0].AsFloat[1, 0] - 1234.56) < 0.001); // 浮点别用等号比 finally XLS.Free; end; end;索引依旧是先列后行、0 基。浮点比较用容差而不是等号,因为单元格里的数值经过一次序列化再反序列化,末位可能出现极小误差,用等号比会误判成失败。Clear调两次是有意的:第一次清掉新建实例的默认内容,第二次在读之前清掉写入时的残留,确保断言比的是文件内容而不是内存里的旧值。
如果手上没有 Delphi 环境可以交叉验证,或者想确认文件结构没被写坏,xlsx 本质上是个 zip,解开来直接看 xml 是最硬核的验证方式:
cd /d d:\tmp copy roundtrip.xlsx roundtrip.zip tar -xf roundtrip.zip -C roundtrip_x type roundtrip_x\xl\worksheets\sheet1.xml能解出xl\worksheets\sheet1.xml,并且里面能看到刚才写进去的文本,说明文件结构是完整的;解压报错或者目录里没有 worksheets,基本可以判定写入过程被打断或者格式根本没对上。这招在客户现场没有 Excel 可比对时特别好用。
| 验证项 | 期望结果 | 失败先查哪里 |
|---|---|---|
| 编译通过 | 无 Unit not found | 对应平台的 Library path |
| 组件创建 | 不抛异常 | 包的位数是否与 IDE 匹配 |
| 写盘成功 | 目标目录出现文件且大小非 0 | 目录权限、文件是否被占用 |
| 读回比对 | 字符串与数值都能对上 | 索引顺序、ANSI 与 Unicode 转换 |
| 外部打开 | Excel 或解压工具无告警 | 扩展名与实际格式是否一致 |
这套回环我只花五分钟,但它能把“路径配错”“位数不对”“格式不符”“编码丢失”四类问题一次性分开。从那以后我每次给新机器装这类包,不管多急都强制走一遍写盘再读回,绝不靠“组件面板出来了应该就好了”这种判断。希望帮到你。
本文还有配套的精品资源,点击获取