
简介Delphi 平台知名的 TMS Component Pack v8.3.4.0 控件套件内含全部源码覆盖表格、图表、报表、文档处理等常用业务场景面向需要跨版本维护桌面项目的中高级开发者。原厂版本支持至 XE10.2本包经修改后可在 XE10.3 中正常安装运行实测可用解决了升级开发环境后控件无法加载的兼容性问题。压缩包共两千余个文件约166.45MB以源代码、编译单元、头文件为主体同时包含窗体、资源、工程文件、示例程序与说明文档目录结构清晰便于按需检索。截至目前已有697人学习下载。读者既可通读全部源码理解控件实现原理也可直接安装集成到各版本工程中复用兼顾源码研究与工程落地适合需要统一控件库、开展组件二次开发或排查多版本兼容性问题的开发团队与个人程序员。 上周帮同事把一个 Delphi 7 的老项目整体平移到 XE10.3顺手把开发机上的 TMS Component Pack 统一换成了 v8.3.4.0 full source 版本。同事盯着安装界面问都这年头了你还折腾这套老掉牙的东西我说你只看到版本号老却没体会过同一套源码从 D7 一路编译到 XE10.3 是什么体验也没感受过组件内部行为不对时、源码就在手边直接开断点往下追的踏实感。这篇文章就把我这些年用 TMS Component Pack 的核心心得整理出来重点说清楚 D7~XE10.3 这个跨度意味着什么、full source 版比 dcu 版多出来的实际价值以及装完以后最容易在哪些位置翻车。适合正在维护老 Delphi 项目、纠结要不要换组件版本、或者第一次接触 TMS 完整源码包的开发者。1. 为什么一套组件能从 D7 撑到 XE10.3先说兼容性的真实边界1.1 十几年的跨度是条件编译堆出来的同一套源码D7 是 2002 年的产品XE10.3Rio差不多是 2018 年发布的中间隔了十几年。TMS Component Pack 8.3.4.0 的定位恰好覆盖这段 VCL 开发者的黄金期同一套源码放进去既能喂饱老旧的 D7也能伺候 XE10.3。很多后来接触 Delphi 的人对版本跨度大没什么概念以为就是打开工程文件直接编译这么简单实际上 TMS 源码里塞满了按 IDE 版本区分的条件编译块。Delphi 2009 引入 Unicode 是一个巨大分水岭D7 时代到处是 AnsiString 和 PChar 的假设2009 之后整个 RTL 字符串模型都变了。TMS 要在同一套单元文件里兼容这两类环境就必须维护大量{$IFDEF UNICODE}、{$IF CompilerVersion ...}之类的判断。XE2 又引入 Win64 平台单元引用、指针类型、整数类型在 32/64 位下都有差异。所以你打开某个 TMS 源码文件如果看到一半是各家编译器版本的条件分支不用惊讶这些不是给客户看的花架子是能让一套组件跑遍十几年的地基。1.2 D7~XE10.3不等于万能边界到底在哪这句D7~XE10.3很容易让人产生错觉所有组件在这区间所有版本上都应该长得一样、行为一致。实际不是。个别依赖较新系统能力的控件在 D7 里会被条件编译直接屏蔽掉面板上根本看不到反过来一些老组件在 XE10.3 里也可能因为系统 API 变化而出现绘制差异。所以正确的姿势是以项目最低 IDE 版本为准只要最低版本能完整编译并安装所有包其他版本基本不会出大问题如果反过来以最高版本为准拿到 D7 上大概率要删掉一批组件。还有一个很实际的问题XE2 之后 VCL 开始支持 Win64但很多老项目直到今天还在 Win32 上跑。8.3.4.0 这套组件在 Win64 下能不能用我的经验是核心的网格、编辑、日期控件编译没问题但冷门组件必须逐个验证。不要想当然认为源码一样、平台只是换一套编译器参数64 位下的指针、消息参数、结构体对齐和 32 位差别很大。1.3 full source 和 dcu-only 的真实差别没用过 full source 版的人第一反应是反正编译出来功能一样要源码干嘛。平时确实一样但一旦控件行为和你预期不一致差别就出来了。dcu 版只能黑盒调试报错、闪烁、绘制乱掉要么猜要么上网搜要么绕路。而完整源码版可以直接走进控件内部看它的鼠标消息处理、绘制状态机、属性赋值逻辑问题定位从撞运气变成读代码。对于 TMS 这种自绘控件占比很高的包来说源码的价值几乎是决定性的。2. 装包的正确姿势从目录规划到IDE配置全流程避坑2.1 环境准备与目录规划TMS 8.3.4.0 的源码解压后第一件事不是急着打开工程而是先把目录结构规划好。我的习惯是单独建一个无中文、无空格的目录比如D:\Components\TMS\8.3.4.0然后在里面把输出目录也分开BPL\D7、BPL\XE10_3、DCP\D7、DCP\XE10_3。这样不同 IDE 生成的二进制文件不会相互覆盖切换项目时也不用担心 IDE 加载到错误版本的包。千万不要把组件放在桌面或者带空格的路径里D7 的 IDE 对路径解析能力真的很弱一个空格就能让 BPL 加载失败排查起来非常绕。另外各版本源码不要试图混用每个 IDE 版本打开自己对应的包工程目录隔离是省事的关键。2.2 编译顺序先 Runtime 后 Designtime先 32 位后 64 位TMS 的包在工程组织上分成运行时包和设计时包两组。运行时包是控件真正的实现设计时包负责把组件注册到 IDE 的组件面板上。顺序很重要必须先编译运行时包再编译设计时包。如果先装设计时包IDE 会找不到对应的运行时类安装会报错或者面板上出现一堆问号组件。具体操作上打开对应 IDE 版本的工程文件通常目录下会有按版本区分的 .dpk 或 .groupproj先把所有运行时包编一遍得到 dcu、dcp、bpl 文件然后在设计时包的工程选项里确认注册方式编译后通过 Component Install Packages 添加生成的 BPL。需要 64 位支持的话再把平台切换到 Win64 重新编译一套Win32 和 Win64 的包不能互相复用。2.3 我踩过的四个坑坑一切换 Delphi 版本后 dcu 串包。症状是编译时突然报某个单元是用不同版本的 System.Types 编译的。多半是因为源码目录里残留了上一个 IDE 生成的 dcu当前 IDE 按 Lib 路径搜到了旧文件。解决办法很粗暴把源码目录及输出目录里的 dcu、dpu、dcp、bpl 全部清掉重新编译。坑二BPL 路径缺失。安装完包IDE 启动时提示找不到某个 bpl 模块。这是 Windows 搜索不到 BPL 所在目录导致的。把输出 BPL 的目录加到系统环境变量的 PATH 里或者在 IDE 的 Tools Options 里设置环境变量指向它重启 IDE 就好。坑三Class already exists。多版本 BPL 同时注册时常见老版本设计时包没卸载干净新版本安装时类名冲突。处理方式是把旧的已安装包先从 IDE 里移除并删除对应 BPL 文件再安装新的。坑四装完面板上找不到组件。九成是只编译了运行时包设计时包没装还有一成是 Tool Palette 的 Filter 没显示出来。到 Component Install Packages 确认设计时 BPL 在列表里并且已勾选再搜一下Adv关键字大部分 TMS 组件都能看到。3. 从读Excel到ClientDataSet联动几个高频场景的组件选型实录3.1 读Excel网格的导入能力比我预想的强热搜里delphi 读取excel一直居高不下说明这需求在传统 Delphi 业务系统里有多常见。TMS Component Pack 的 TAdvStringGrid 自带 Excel 数据导入能力也支持把 Excel 复制到剪贴板的内容直接粘贴到网格里。导入后网格的单元格格式、跨列显示、合并单元格虽然做不到 Excel 编辑器那种完整还原但把一张二维表读进来做预览、编辑、回写是没问题的。我用它处理过一个信贷系统里的导入场景客户把征信表粘到 Excel系统再把这堆数据批量接到审核界面。以前用 OLE 一个个单元格读几百行数据能转好几秒换 TMS 网格的导入方式后速度明显提升而且不用在客户机器上依赖 Office 是否安装。要注意的是如果业务里需要精确处理公式、图表、复杂格式那就别指望组件包里的网格导入直接上 FlexCel 这类专门组件更合适。3.2 ClientDataSet.CloneCursor 和 TMS 网格的联动有人问 TClientDataSet 里的 CloneCursor 函数是干什么的我简单解释一下它能让两个或多个 TClientDataSet 共享同一份数据快照字段结构、内存数据都是同一份一个数据集移动游标另一个也会跟着变。经常用在同一个数据集需要在主窗口和弹窗里同时展示、或者需要保留两个不同视图的场景。第二个参数 Reset 传 False 时保留目标数据集现有的过滤、索引等设置传 True 时把这些都清掉。我实际的项目里主窗体用 TMS 网格展示汇总列表双击某行弹出一个详情窗体详情窗体绑定的正是主 CDS 克隆出来的另一个 CDS。同一份数据主窗体高亮选中详情窗体只读展示数据源是一个不需要重复查询也不用担心两边数据不一致。TMS 网格只关心 DataSource 有没有数据这个组合非常顺手。// 复制一份数据游标两个 CDS 共享同一份数据 CDSDetail.CloneCursor(CDSMaster, False);3.3 日期控件与周六日判断别把简单问题复杂化判断周六日属于典型的看起来要写一堆、实际一行搞定的需求。Delphi 的 RTL 里 DayOfWeek 返回 1 到 7周日是 1周六是 7。行业里很多项目习惯用周一作为一周起点那可以直接用 DayOfTheWeek它返回 1 表示周一7 表示周日。TMS 的日期时间控件拿到用户选择的 TDateTime 后把值传给 DayOfWeek 就行。我之前在排班模块里给 TMS 网格做了周末列底色高亮在单元格绘制事件里判断当前日期落在周六周日就换一种背景色。代码不复杂但效果很直观业务人员一眼能看到哪些天没人值班。这种小需求其实不需要引入任何重型组件关键是把日期 API 记准确。uses System.DateUtils; if DayOfWeek(ADate) in [1, 7] then // 周六或周日3.4 WebBrowser缩放、控制U盘、CAN口这类需求别指望一套控件包通吃热搜词里还有 WebBrowser 控制放大缩小、禁用 U 盘、CAN 口编程这类问题这些和 TMS Component Pack 的关系真的不大。TMS 是业务界面组件解决的是窗体、表格、输入、日期、导航这些偏业务层的需求WebBrowser 缩放操作的是浏览器内核的 IOleCommandTarget 命令通道通过给 IDM_ZOOM / IDM_ZOOM_PERCENT 这类命令传参数就能实现页面按比例缩放。U 盘禁用要么走设备管理/注册表策略要么做驱动层拦截CAN 口通信大多走串口或者专用总线库。我的原则很简单选型之前先问自己这个问题是界面层还是系统层。界面层找 TMS 这类控件库系统层老老实实查 Windows API、写协议、调驱动。拿着锤子找钉子最后只会把自己搞得很难受。3.5 局域网SendMessage、UniGUI会话超时这类场景同样要看清边界局域网从一个程序发消息给另一个程序这类需求底层用的是 Windows 消息机制简单场景用 SendMessage / PostMessage 配合 WM_COPYDATA 或自定义消息就能做复杂场景走命名管道、TCP 都行这些不属于 TMS 的范畴。UniGUI 的会话超时跳转主页则是 Web 框架自己的配置逻辑更是和 VCL 组件包八竿子打不着。我的建议是脑子里先分清这个功能该由谁负责再动手否则容易在错误的框架里找答案。4. 完整源码版到底多了什么Debug、改包、学底层4.1 进入组件内部单步调试我举一个自己遇到的例子。用 TAdvStringGrid 做一个带图标的树形列表每次刷新数据时最后一列总会出现零点几秒的残影肉眼能看出背景没擦干净。dcu 版遇到这种问题只能先怀疑绘制事件里哪里写错反复检查自己的代码却找不到问题。换成 full source 版之后我直接在组件内部的自绘代码里设断点看 Canvas 的裁剪区域保存、恢复顺序一下就定位到是内部处理选中状态时 ClipRect 的恢复时机不对导致重绘背景时只画了一半。这种体验在自绘控件身上特别明显。自绘控件的绘制代码量大状态多出了问题如果是自己的代码还好查一旦是组件内部的逻辑没有源码就只能靠试。而组件供应商也不一定会为老版本提供持续修复源码就是最后一道保障。4.2 改源码做定制客户经常提一些组件默认不支持的小需求。比如 TMS 网格的过滤下拉框默认是英文业务方要求全中文。full source 版可以直接改过滤面板的字符串常量重新编译设计时包装到 IDE 里项目一起构建。这个能力的价值不在于改一次很爽而是能让最终交付物和业务语言完全对齐。改源码要注意三件事一每次 TMS 版本升级后自己的改动会被覆盖务必用 diff 或补丁方式管理不要直接改原件后不留痕迹二license 协议一般要求保留版权声明别把原作者的标识删掉三定制的代码必须在所有目标 Delphi 版本下重新编译验证防止条件编译分支没覆盖到。4.3 学习VCL控件体系的绝佳教材Delphi 老玩家经常说读控件源码比读万卷书有用这话在 TMS 源码上体现得很充分。它能教你自绘控件如何响应 WM_Paint、WM_EraseBkgnd如何写设计期属性编辑器如何实现属性流的持久化以及如何在同一个源码文件里用{$IFDEF}兼容不同编译器版本。这套能力是普通项目业务代码根本不会涉及到的层次。如果你正卡在会写窗体、不太懂控件底层的阶段把 TMS 某个组件的核心单元从头到尾读一遍再配合单步调试走一遍绘制流程你对 VCL 的理解会上一个台阶。D7 到 XE10.3 的跨度越大这份源码能展示的环境差异和适配技巧就越多。5. XE10.3 之后的路怎么走升级还是留守5.1 新Delphi版本来了老包怎么办值得注意的是热搜里已经出现 Delphi 13.0 Florence、Delphi 13.1 这类字眼。从 XE10.3 再往后Delphi 版本号确实又走了一截。TMS Component Pack 8.3.4.0 作为止步于 XE10.3 时期的版本拿到新 IDE 里很难直接编译通过大概率会遇到 dcu 版本不符、编译器级功能不识别等问题。如果必须用新 Delphi通常只有两条路升级到新版的 TMS 组件包后续的 VCL UI Pack 或 TMS Component Pack 新版本或者自己把源码用条件编译适配到新环境。后者的工作量要看项目用了多少组件。如果只是一两个常用控件适配成本可控如果把整个包都背上新版本维护成本会非常可观。我一般不推荐老项目为了追新 IDE 而勉强适配大包除非有非升不可的理由。5.2 我的迁移思路老项目守着老版本其实是正常的。只要目标系统还能正常编译、部署组件版本稳定、源码在手8.3.4.0 完全可以陪着老项目走到退役。新项目则相反直接上当前最新的 Delphi 和新版 TMS从起跑线上就不给自己添堵。跨版本迁移的时候建议先把 TMS 组件封装在 Form 或 Frame 层别让业务代码到处直接引用组件类。这样将来升级组件或替换实现时影响面能被控制住而不是牵一发动全身。我在实际迁移里会把项目依赖的组件列一个清单按使用频率排序一个一个替换验证。替换之后再跑一遍完整回归确保绘制、绑定、焦点行为没有变化。这套流程虽然慢但很稳。最后再分享一个小技巧一台机器上装多个 Delphi 版本时库路径最不容易打架。我给每个版本单独建一个启动脚本在里面设置好 BDS、BPL、DCP 路径后再启动 IDE。D7 项目归 D7XE10.3 项目归 XE10.3谁也碰不到谁的包。TMS Component Pack 8.3.4.0 的 full source 版在我这里服役了很长时间直到现在维护那些老项目时仍然依赖这套环境。如果你也在那条老代码还在跑、新需求不断来的船上这套配置思路应该能帮你省下不少折腾时间。本文还有配套的精品资源点击获取