ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

D7到XE7跨版本迁移:DevExpress 14.1.3组件库安装与编译避坑指南

D7到XE7跨版本迁移:DevExpress 14.1.3组件库安装与编译避坑指南

简介:DevExpress 14.1.3 for D7-XE7是一套面向Delphi 7与XE7开发者的第三方组件库,覆盖桌面与移动跨平台场景,提供数据网格、图表、报表、菜单、工具栏、对话框等丰富的可视化控件,可帮助程序员显著提升应用界面开发效率与程序运行性能。压缩包以7z格式封装,大小约190MB,内含主安装文件、VCL组件源码以及DEV汉化Demo,方便中文用户直接集成到Delphi环境中,并借助示例快速理解控件属性和事件用法。该资源已有137人学习,适合中高级Delphi程序员用于项目中的界面层快速建设。获取本包后,开发者既能获得经过原生代码优化的完整控件库,又能参考汉化示例减少摸索成本,从而将更多精力投入到业务逻辑实现与产品体验打磨上。这些控件在设计时和运行时均支持高度自定义,可适应多种界面风格需求,是一套实用且值得收藏的控件资源。

1. 老项目卡壳时,搜索框里打的是什么

你接手一个跑了快十年的 D7 项目,领导只丢下一句话:迁到 XE7。代码迁移本身不难,难的是那套界面组件库——标题里的 DevExpress 14.1.3 for D7 - XE7 不是某一个控件,而是一个版本代次:它同时覆盖 Delphi 7 到 XE7 之间的多个 IDE 版本,安装器根据你机器上已有的 IDE 生成对应后缀的包。14.1.3 是许多老项目锁定的“能用的新版本”,往下功能太老,往上控件行为大变样。这篇文章要解决的,就是这个版本代次怎么装、怎么配、哪里容易翻车,以及如何在 D7 和 XE7 之间不互相污染地共处。

2. 吃透 14.1.3 的版本盘:同一个安装包如何同时搞定 D7 和 XE7

2.1 后缀数字认的是 IDE 代号,不是安装日期

很多人第一次见到 “14.1.3 for D7 - XE7” 会以为是两套独立安装包,一套给 D7,一套给 XE7。其实不是。它是一套源码,在编译时通过条件编译选择代码分支,于是同一个源文件在 D7 里走 AnsiString 老分支,在 XE7 里走 UnicodeString 新分支。安装器真正做的事情,是检测你机器上装了哪些 IDE,然后生成对应后缀名的预编译包。

拿到安装包后第一件事不是双击安装,而是打开 Lib 或 Packages 目录,按文件名后缀排序。你会看到一排带 D 字母的文件夹或文件:D7、D12、D14、D15,一直到 D21。这个后缀对应的是 IDE 对外显示的产品版本号,不是编译器内部版本号,也不是发布日期。把带 dcl 前缀的文件单独放一排,剩下的就是运行时包;后缀相同的 dcl 包和运行时包才是一对。这是安装器将要注册进 IDE 的内容清单,也是后面排查缺包问题时的参照物。

IDE 版本包文件常见后缀说明
Delphi 7D7ANSI 时代,条件编译走最老分支
Delphi 2009D12从这代起默认 UnicodeString
Delphi 2010D14产品版本号跳过了 13
RAD Studio XE2D16引入 64 位编译,控件库开始分平台
RAD Studio XE7D21标题范围里最右侧的 IDE

实际审查后缀时,不要把 D7 的 “7” 和 XE7 的 “7” 混为一谈。D7 是 Delphi 7 的专属后缀,XE7 对应的后缀是 D21。同一个 bpl 文件名里出现 D7 只能说明它属于 Delphi 7,不代表它兼容 XE7。这个混淆是我见过的最常见误判。

2.2 dcl 前缀拆开看:设计时包和运行时包是两套东西

组件库的落地依赖了两层包结构。不带 dcl 前缀的是运行时包,例如 dxCoreD21.bpl,它编译进你的 EXE 或作为依赖被 EXE 引用;带 dcl 前缀的是设计时包,例如 dclDxCoreD21.bpl,它只服务于 IDE 本身,负责把控件注册进工具箱、设置属性编辑器、处理设计期行为。安装组件到 IDE,实际加载的是 dcl 包,而 dcl 包又依赖同名不带前缀的运行时包。

这个依赖链决定了编译顺序和排查方向。如果你只把 dcl 包扔进 IDE 而对应的运行时包不存在,安装器会给一条 “Cannot load package” 的提示,然后这次加载被整体回滚。所以手动编译时要先编译运行时包,再编译设计时包,顺序反了就会看到一大堆找不到文件的报错。给 XE7 编译时,也不要凭记忆在 IDE 里挨个选择文件,直接按 D21 后缀去核对所有 bpl,就能确认这一层是否齐整。

2.3 为什么老项目愿意锁 14.1.3 而不是装最新版

最新的组件库版本会改变控件行为,而且这类改变是静默的。网格的取色器、打印引擎的默认字体、皮肤机制、筛选面板的默认按键,每一样都可能被老业务流程依赖。换新版本意味着把当年测过的界面全部回归一遍,对维护旧系统的人来说成本极高。锁定 14.1.3 就是给自己留后悔药:源码分支固定,条件编译符号固定,预编译包的目录结构也固定,出问题能查、能退回。

第三位小版本号 3 在这个标题里也有意义。常见做法是只取大版本加补丁号,因为补丁号通常修复了前几个小版本在特定 IDE 上的编译问题。如果你手里的安装包是 14.1.3,且同时存在其他小版本,优先保留 14.1.3;如果已经装了 14.1.0 或 14.1.2,且没有出过触发你业务的问题,也不一定非要升级。老项目讲究的是稳定跑,不是追新。

3. 装包前先立规矩:目录、检测与组件清单

3.1 目录规划:根目录、源码目录、预编译目录分开

DevExpress 14.1.3 这类老版本组件库的安装器,默认目录往往把所有东西堆在一处。我一般会先手动规划一个根目录,把源码、预编译库、最终 bpl 分成三层,避免 D7 和 XE7 的产物互相踩踏。

D:\DevExpress 14.1.3 ├─ Sources\ 原始 .pas/.inc/.res,只读 ├─ Lib\D7\ D7 的预编译 DCU 和 BPL ├─ Lib\D21\ XE7 的预编译 DCU 和 BPL └─ Bin\ 安装器或手动编译最终输出的 bpl

Sources 目录一旦安装完就不再改动;Lib 下的子目录按 IDE 后缀隔离;Bin 目录用来放置真正需要被 IDE 注册的设计时包。这样做的直接好处是,给 XE7 增加包路径时不会把 D7 的 DCU 拖进来,反向也不会。不要图省事把两个 IDE 的库路径指向同一个目录,否则 IDE 会随机选中某一个版本的 DCU,编译选项稍微变化就出现类似 Invalid or missing dcu 的玄学报错。

3.2 安装器是怎么识别 IDE 的:注册表视角

安装器判断系统里装了哪些 IDE,靠的是注册表里的安装信息。D7 的路径在 HKCU\Software\Borland\Delphi\7.0 下,XE7 在 HKCU\Software\Embarcadero\RAD Studio<具体版本号> 下。在 64 位系统上,32 位安装器读取的注册表视图和 64 位视图并不完全一致,老 IDE 的键一旦只存在于另一个视图,安装器就会显示“未检测到 IDE”或漏掉 D7。

遇到这种情况不要死磕安装器,常见做法是先打开注册表检查两个视图下的键是否都存在,然后把缺失的键补齐,或者干脆跳过安装器的自动检测,只让安装器复制文件,后续用手工编译注册。补注册表键要小心,不要为求快把整个 IDE 的安装键改写,那样反而会把 IDE 自身弄坏。如果机器上同时有 D7 和 XE7,建议先装 D7 再装 XE7,因为老的安装器对老版本的识别逻辑更稳定。

3.3 组件勾选清单:别把整套组件库坨进工具箱

DevExpress 14.1.3 是一个大集合,包含了网格、调度器、电子表格、编辑器、皮肤库等一堆产品。全装的代价是 IDE 启动变慢、工具箱选项卡被占满、不同产品之间的事件响应器互相注册。按业务需求勾选才是正确姿势。

组件组建议理由
网格与数据编辑器常装老系统里查询、录入、报表几乎离不开
菜单与工具栏常装界面改造最先用到,依赖其他核心包
调度器/排程组件按需只有日历排程业务才值得引入
电子表格控件慎装与老打印引擎冲突较多,无明确需求别碰

上面这张清单不是教条,思路是“能少装就少装”。等编译报缺包时再回来补装对应产品,代价远比一开始全装要低。安装器里如果支持按产品勾选,就只勾业务会用到的那几项;如果不支持,至少也要手动把不需要的设计时包从注册里移除,保留运行时包不动。

4. 编译 dcl 包与注册:把组件库正式装进 IDE 的完整路径

4.1 先确认编译器版本,再谈编译包

手动编译前,最常见的翻车点是拿错编译器。D7 有自己的 dcc32.exe,XE7 也有自己的 dcc32.exe,两者对不同语言版本的源码处理完全不同。与其猜文件路径,不如先写一个最小的编译验证程序,分别用两个 IDE 的编译器各跑一遍,看输出的 CompilerVersion 值对不对。

program CompilerCheck; {$APPTYPE CONSOLE} uses SysUtils; begin WriteLn('CompilerVersion = ', CompilerVersion); ReadLn; end.

用 D7 打开这个工程再编译,正常会输出 15;用 XE7 打开再编译,输出应该是 28。这个数值是后面写条件编译和核对包后缀的依据。如果 D7 下输出不是 15,说明系统 PATH 里的 dcc32.exe 被某个新版本 IDE 的编译器截胡了,这就是后续所有编译报错的根因。把 IDE 的 Bin 目录显式写进脚本,不要让系统 PATH 去碰运气。

4.2 命令行编译:构建顺序与 dcc32 参数

手工在 IDE 里逐个打开 dpk 编译不是不行,但重复操作多、容易漏。常见的做法是写一个批处理,先编译运行时包,再编译对应的 dcl 设计时包。下面是一个给 D7 编译的示例,重点看参数和顺序。

@echo off rem 用 D7 编译运行时包,再编设计时包 set DX_ROOT=D:\DevExpress 14.1.3 set D7_DCC=C:\Delphi7\Bin\dcc32.exe set DX_UNITS=%DX_ROOT%\Sources\DevExpress.Core;%DX_ROOT%\Sources\DevExpress.Utils;%DX_ROOT%\Sources\DevExpress.Data echo Build runtime package... "%D7_DCC%" -B -Q -E%DX_ROOT%\Lib\D7 -U%DX_UNITS% %DX_ROOT%\Packages\DevExpress.Utils.dpk echo Build design-time package... "%D7_DCC%" -B -Q -E%DX_ROOT%\Lib\D7 -U%DX_UNITS% %DX_ROOT%\Packages\dclDevExpress.Utils.dpk

这里 -B 表示强制重新编译,避免残留的 DCU 影响结果;-Q 是静默输出,把警告和错误之外的过程信息压掉,方便快速定位报错;-E 指定 bpl 输出目录;-U 指定单元搜索路径。实际文件名以你安装包里的 dpk 为准,不必严格沿用示例名称。编译设计时包之前,必须先把它的所有依赖包编译好,否则 dcc32 会在 require 阶段直接失败。给 XE7 编译时,把 D7_DCC 换成 XE7 的 dcc32.exe,单元路径和输出目录中的 D7 全部改成 D21,逻辑完全一致。

4.3 把 bpl 交给 IDE:两条可复用的注册路径

编译产物落地后,需要让 IDE 认识设计时包。最直观的方式是 IDE 菜单里的组件安装对话框,手动选择 bpl 文件;但批量操作时我会直接写注册表。D7 注册设计时包的主键是 HKCU\Software\Borland\Delphi\7.0\Known Packages,每个设计时包对应一个字符串值。

Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Software\Borland\Delphi\7.0\Known Packages] "D:\\DevExpress 14.1.3\\Lib\\D7\\dclDevExpress.UtilsD7.bpl"="DevExpress Utils D7"

XE7 的注册主键在不同版本下数字不同,写脚本前先在注册表里搜索 RAD Studio 即可确认。这种注册表方式适合把安装步骤沉淀成脚本,团队其他人克隆环境时不用再手动点一遍 IDE 对话框。不过注册表只是把 bpl 路径告诉了 IDE,bpl 文件本身必须真实存在,且它依赖的运行时包也要能在系统 PATH 或库路径里找到,否则 IDE 启动时会弹缺失包的错误。

4.4 库路径与搜索路径:两者职责完全不同

编译配置是新手最容易迷糊的区域。库路径让 IDE 找到预编译的 DCU 和 BPL,搜索路径让编译器找到源码 .pas 文件。D7 和 XE7 在 IDE 选项里都区分这两项,名字可能略有不同,但职责一致。

路径类型作用本场景里的建议值
库路径 Library Path让 IDE 和编译器找 .dcu/.bplLib\D7 或 Lib\D21
搜索路径 Search Path让编译器找 .pas 源码Sources 下对应产品目录
浏览器路径 Browsing Path处理代码导航,可选默认即可

如果你手上是预编译包,只配库路径就够了,别把整个 Sources 都加进搜索路径;加了反而可能让编译器把该用预编译 DCU 的地方重新用源码编译,产生版本不一致。如果你从源码自己编译全部包,那么搜索路径里必须包含源码目录,且顺序放在库路径之前,这样才能保证编译器看到的是当前这份源码,而不是别的目录里残留的旧 DCU。

5. 避坑:14.1.3 从 D7 到 XE7 最容易翻车的五个现场

5.1 安装器没识别出 XE7,只列了 D7

现象:安装向导启动后,IDE 列表里只有老版本,XE7 那一项是灰的或直接不显示。原因:XE7 的注册表安装键缺失,或者安装器读取的注册表视图不对,在 64 位系统上尤为常见。解决:先在注册表里确认 RAD Studio 的键存在,如果缺失就在 IDE 里打开一次并用正常方式关闭,让 IDE 自己重建安装键;之后重新运行安装器。如果还是认不出来,干脆让安装器只复制文件,后续模块都走手工编译注册流程。

5.2 编译能过,运行时报找不到 dx 开头的 bpl

现象:工程编译正常,双击 EXE 却提示找不到 dxCoreD21.bpl 之类的文件;IDE 里运行同样报错,但界面上已经能看到控件。原因:工程启用了 Runtime Packages,EXE 不包含组件代码,运行时按系统 PATH 和应用程序目录找 bpl。开发机路径恰好配过,所以 IDE 里正常;换到未配置的机器或从命令行启动就露馅。解决:把对应 IDE 后缀的 bpl 目录加入系统 PATH,或把需要用到的 bpl 复制到 EXE 同目录;更稳妥的做法是直接关闭 Runtime Packages 静态编译,让 EXE 自己带组件代码,代价是体积变大。

5.3 设计包装上了,工具箱里却一片空白

现象:组件安装对话框里能看到 dcl 包,勾选也正常,但工具箱里找不到新增的选项卡和控件。原因:dcl 包加载时依赖的运行时包没有先加载,IDE 抛出的错误被记录在消息日志里,而界面上的表现为静默失败;有时是因为运行时包版本和设计时包后缀不一致,加载被整体回滚。解决:先检查 IDE 消息日志,找到 “Cannot load” 的具体包名,按依赖关系把运行时包补齐,再重新注册 dcl 包。不要反复勾选同一个 dcl 包,那不是问题所在。

5.4 D7 打不开 XE7 保存过的 DFM

现象:同一个表单在 XE7 里保存后,回到 D7 打开提示某个属性不存在或类型不匹配,整个窗体打不开。原因:XE7 在 DFM 里写入了 D7 不认识的新属性,尤其是组件库在 XE7 下的新皮肤、新对齐属性。解决:如果团队里两代 IDE 同时在用,约定以最低版本 IDE 的环境为准来保存 DFM;或者用文本格式 DFM,在打开前手工删除高版本新增的属性。这种方式费时且容易手滑,真正干净的做法是用版本控制工具维护两份 DFM,按各自 IDE 的构建配置去编译。

5.5 新老版本组件库装进同一个目录,互相污染

现象:先装了 14.1.3,后来又装了其他大版本的组件库,老工程编译时出现 DCU 版本对不上、bpl 加载失败。原因:两个大版本的库路径指向同一个目录,IDE 在库路径里找到了同名但版本不同的 DCU,先加载到谁就用了谁。解决:每个大版本单独一个根目录,不要共用一个 Lib 目录;IDE 的库路径只保留当前项目对应的那一个版本。这条规矩看着简单,很多开发者都是在连续编译通过但运行崩溃后才意识到,那已经浪费掉半天时间了。

6. 验证与收尾:用脚本把两套环境练顺手

6.1 用一个小程序读 bpl 的版本资源,确认没有装错

光看文件名不能完全确认 bpl 是不是装错版本,因为版本号可能在编译时被覆盖。更稳的做法是读 bpl 自身的版本资源,把期待值直接打印出来。

uses Windows, SysUtils; function GetBplVersion(const AFileName: string): string; var Dummy, Size, Handling: DWORD; Buf: Pointer; Fixed: PVSFixedFileInfo; begin Result := ''; Size := GetFileVersionInfoSize(PChar(AFileName), Dummy); if Size = 0 then Exit; GetMem(Buf, Size); try if GetFileVersionInfo(PChar(AFileName), 0, Size, Buf) then if VerQueryValue(Buf, '\', Pointer(Fixed), Handling) then Result := Format('%d.%d.%d', [ HiWord(Fixed.dwFileVersionMS), LoWord(Fixed.dwFileVersionMS), HiWord(Fixed.dwFileVersionLS)]); finally FreeMem(Buf); end; end;

D7 里用的单元是 Windows,XE7 里换成 Winapi.Windows 即可。把函数参数替换成 dxCoreD21.bpl 的实际路径,输出版本号和 14.1.3 对应,才能断定这条链上的包没有混入其他代次的产物。这个验证动作应该在每次重新编译后都跑一次,而不是只在第一次配置时跑。

6.2 一个批处理脚本,切换当前命令行的组件库版本

日常维护里,D7 和 XE7 会在同一个工程目录轮流转。为避免每次打开一个 IDE 都去改全局 PATH,我会在工作区根目录放一个版本切换脚本,按需设置当前控制台的搜索路径。

@echo off rem 切到 XE7 时把后缀改成 D21 set DX_ROOT=D:\DevExpress 14.1.3 set IDE_SUFFIX=D21 set LIB_PATH=%DX_ROOT%\Lib\%IDE_SUFFIX% set PATH=%LIB_PATH%;%DX_ROOT%\Bin;%PATH% echo Current bpl path: %LIB_PATH%

这个脚本不强改系统环境,只影响当前打开的终端窗口。你可以在里面继续拼接 IDE 的 Bin 路径,保证在这个终端里调用的 dcc32 就是目标 IDE 的编译器。这样 D7 和 XE7 的命令行编译环境互不干扰,配合前面的 CompilerCheck 工程,很容易确认当前窗口到底在操作哪套环境。

6.3 最后的习惯

我给自己定了一条不成文的规矩:每次装完组件库,先建一个最小 SmokeTest 工程,放一个网格、一个导航栏、一个打印预览组件,把设计时拖拽、运行时打开、打印预览三个动作全跑一遍,再打开业务系统。这个工程跑通,不代表所有业务窗口都没问题,但至少把组件库自身最常见的问题在第一步就拦住了。这个习惯救过我很多次,包括第一次用 14.1.3 同时支持 D7 和 XE7 时,正是 SmokeTest 在 30 秒内暴露了 dcl 包依赖顺序的问题,省下了直接对接业务后排查的两小时。希望你也能在自己的环境里留这样一个安全网。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进