首页 / 资讯中心 / 文章详情

Delphi XE11下JCL 2.8/JVCL 3.5安装编译与配置持久化

Delphi XE11下JCL 2.8/JVCL 3.5安装编译与配置持久化 ★ FEATURED ARTICLE
简介面向 Delphi XE11 开发者的 JCL 2.8 与 JVCL 3.5 组件与源码包适合需要扩展 VCL 界面能力、复用底层工具函数、维护旧版 Delphi 项目或进行跨平台原型验证的开发者。压缩包共 2000 个文件、约 45.15MB包含 dpk/dproj 安装工程、pas 源码单元、dfm 窗体、res/rc 资源、bmp/ico/png 图像以及 txt/xml/ini 配置文档等并附带 HELP 帮助文件便于查阅组件用法、函数说明、安装注意事项与示例代码。已有 347 人学习或下载在 Delphi 控件与工具库场景中具备一定参考价值。JCL 覆盖文件操作、内存管理、日期时间、加密解密、网络通信与错误处理等底层能力JVCL 提供按钮、表格、菜单、对话框以及图表、报表、数据库等可视控件。两者结合可支撑从工具函数封装到界面搭建、功能验证和旧项目兼容维护的完整开发需求目录层级较完整对中高级 Delphi 开发者尤其方便。1. 一套组件包补两个缺口Delphi XE11 下 JCL 2.8 与 JVCL 3.5 的定位一个维护了多年的 VCL 项目要迁到 Delphi XE11编译通常能过界面和底层却开始出问题页签在高分屏下错位配置读写还停在手写 INI 的年代异常日志只有一行 Message。这时不少团队会回头找 JCL 2.8 JVCL 3.5 这套老组合。JCL 补的是字符串、文件路径、系统信息、异常堆栈这类底层工具单元JVCL 补的是几百个现成 VCL 控件外加一组配置持久化组件。它不算新东西但对还在写 Delphi 桌面程序的团队是少数能一次性补齐「底层函数 界面控件 窗体状态保存」的源码级资源。我下面按自己装这套包的顺序把依赖判断、编译顺序、控件落地和翻车点走一遍。2. 依赖关系与版本矩阵为什么必须 JCL 先、JVCL 后2.1 两个包的依赖方向是单向的JCL 是纯单元库交付形态是源码目录加一批运行期包。它不依赖 JVCL单独编译就能用。JVCL 不一样它的运行期包在设计时就引用了 JCL 的单元和包其中最典型的是桥接单元同时 uses 了 JCL 的字符串、系统信息和文件工具。这意味着在 IDE 里对 JVCL 点 Build 之前机器上必须已经存在一个可被编译器解析的 JCL 包并且这个包已经注册到 IDE。顺序错了会得到非常直白的报错E2202 Required package Jcl not found或者编译到某个桥接单元时报F1026 File not found。这两种报错根因是同一个——JCL 没装好。很多人第一次装会先把 JVCL 打开看到满屏红线就以为包本身跟 XE11 不兼容其实是顺序反了。提示包依赖和单元路径是两件事。E2202是包没注册F1026是单元目录没进 Library Path。两条都要修只修一条还是编不过。2.2 XE11 下要先确认的三个兼容点第一个是编译目标。设计期包只能编到 Win32因为 IDE 进程本身是 32 位设计期包要被 IDE 加载目标平台选错就装不进去组件面板也不会出现。运行期包则要 Win32 和 Win64 各编一份否则 64 位版本的程序一链接就报单元找不到。第二个是字符集遗留。JCL 和 JVCL 都是 Unicode 化之后还在维护的老代码里面保留了PChar、AnsiString混用的写法。XE11 编译这类代码时会出现大量W1057、W1058隐式字符串转换警告。这些警告大多数不影响正确性但如果项目开了「警告即错误」就会直接编译失败需要在工程选项里针对这两个包放宽这一条。第三个是汇编优化单元。JCL 里做堆栈跟踪、异常钩子的部分单元含 32 位内联汇编这些单元在 64 位目标下走的是另一套条件编译分支功能会打折。如果你的程序要用异常堆栈先确定主目标平台再决定是否依赖这部分能力别等上线才发现 64 位下堆栈是空的。还有一层是 VCL 主题与高 DPI。JVCL 里有相当多自绘控件绘制逻辑按 96 DPI 的像素值写死。XE11 的窗体默认开启缩放混用之后就会出现控件边缘错位、图标模糊。这类问题不属于编译错误属于视觉验收阶段才会暴露的问题装之前心里要有数。2.3 装之前把目录和路径定死源码目录我一般固定在一个不含空格、不含中文的短路径下例如D:\dev\jedi并且不用系统盘的用户目录。原因是包文件里会记录相对路径路径里带空格时某些旧版安装脚本在拼命令行时会漏引号编译中途失败带中文时部分旧工具链读文件会乱码。这两个坑我都踩过回退成本不高但很浪费时间。下面这段批处理用来在动手前做一次体检确认解压出来的目录结构是完整的。把它存成check.bat双击运行即可输出[MISS]的那一行就是缺的东西。echo off rem 源码根目录按自己的解压位置改 set BASED:\dev\jedi set JCL_ROOT%BASE%\jcl set JVCL_ROOT%BASE%\jvcl for %%D in ( %JCL_ROOT%\source\common %JCL_ROOT%\source\windows %JCL_ROOT%\source\vcl %JCL_ROOT%\install %JVCL_ROOT%\run %JVCL_ROOT%\design %JVCL_ROOT%\common %JVCL_ROOT%\resources ) do ( if exist %%D (echo [OK] %%D) else (echo [MISS] %%D) ) rem 拼一份可直接粘贴到 IDE Library Path 的字符串 echo. echo ---- Library Path ---- echo %JCL_ROOT%\source\common;%JCL_ROOT%\source\windows;%JCL_ROOT%\source\vcl;%JVCL_ROOT%\run;%JVCL_ROOT%\common这段脚本不写注册表、不改环境只做两件事逐个探测目录是否存在以及把要填进 IDE 的库路径拼成一行。BASE改成你自己的解压位置其余不用动。%%D是批处理的循环变量if exist会连引号一起判断所以路径里有空格也能正确识别。库路径该配哪几条按用途拆开看更清楚路径里面的东西是否必须jcl\source\common跨平台公共单元字符串、流、数学必须jcl\source\windowsWindows 专用系统信息、注册表、外壳必须jcl\source\vcl与 VCL 打交道的少量单元用到才加jvcl\runJVCL 运行期包与全部控件单元必须jvcl\commonJVCL 公共单元必须jvcl\resources控件位图、光标等资源不进库路径进资源路径注意最后一行。jvcl\resources放的是二进制资源不是.pas单元把它塞进 Library Path 只会让编译器多扫一遍目录没有任何好处。它应该出现在工程选项的资源目录里或者由包自身的搜索路径带进去。3. 从源码到组件面板编译顺序、目标平台与冒烟验证3.1 先编 JCL安装脚本和手工编译各管一段JCL 的install目录下有一个安装入口常见命名是install.bat不同打包版本略有差异。它的作用是扫描本机已安装的 IDE、自动生成包工程、编译并注册。图省事可以直接跑但它有两个不确定因素一是它对 IDE 版本的识别依赖注册表多版本共存时可能选错二是它默认只编一个目标平台Win64 往往漏掉。我的习惯是脚本只用来干前半段真正的编译和安装放到 IDE 里手工做因为手工做的每一步都可见、可回退。具体做法是在 IDE 里打开jcl\packages下的运行期包工程在项目管理器里把 Win32 和 Win64 两个平台都勾上然后逐个 Build。JCL 的运行期包不止一个按功能切成若干份项目管理器里能看到几个就编几个全部 Build 通过、没有红色错误再往下走。编译通过的标准不是「没有红线」而是输出目录里真的生成了.bpl和.dcp。如果 Build 显示成功但目录是空的八成是输出路径被包的工程选项指到了一个不存在的位置去工程选项的 Output 目录里看一眼改成实际存在的目录再编一次。3.2 再编 JVCLrun 到 design最后才是 InstallJVCL 的结构分两层run目录是运行期包design目录是设计期包。顺序不能颠倒设计期包在 requires 里引用了运行期包运行期包没编出来设计期包一定编不过。操作路径是这样先在run目录下把所有运行期包按 Win32 Win64 编一遍这一步只 Build不 Install然后切到design目录把设计期包按 Win32 编一遍只编 Win32理由前面说过编过之后在项目管理器里右键这个设计期包选 Install。Install 成功后组件面板上才会出现 JVCL 那一页几百个控件才会真正挂到 IDE 里。这里有个容易被忽略的细节Install 只对设计期包有意义。运行期包上右键也能看到 Install点了会把运行时包也塞进 IDE 的包列表后果是 IDE 启动变慢、卸载时多出一堆残留项。运行期包的正确处理方式就是编完放着让设计期包去引用它。包编完了还要回到库路径。打开 IDE 的 Library Path 设置分别在 Win32 和 Win64 两个节点下把前面那张表里的路径填进去。只填 Win32 是最常见的疏漏症状很典型32 位程序编得好好的一切到 64 位就报某个 JVCL 单元找不到。3.3 用两个最小工程验证装好了没有装完之后别急着往正式项目里塞先建两个最小的工程做冒烟。第一个是控制台程序只验证 JCL 的单元能不能被编译器解析第二个是空的 VCL 窗体验证 JVCL 的控件能不能实例化。program CheckJcl; {$APPTYPE CONSOLE} uses System.SysUtils, JclStrings, // 字符串工具 JclSysInfo; // 系统信息 var S: string; begin try S : StrRepeat(, 12); // 重复字符验证 JclStrings 可用 WriteLn(S); WriteLn(OS : , GetWindowsVersionString); WriteLn(Computer: , GetLocalComputerName); WriteLn(JCL units resolved.); except on E: Exception do begin WriteLn(FAILED: , E.ClassName, / , E.Message); ExitCode : 1; // 让构建脚本能感知失败 end; end; end.这段代码的价值在于「编译期就能发现问题」。uses里两个 JCL 单元只要有任意一个找不到编译直接失败不用等到运行时。StrRepeat是最简单的纯函数没有副作用GetWindowsVersionString和GetLocalComputerName分别打一条系统级信息能顺带确认单元确实被链接进来而不是被优化掉。ExitCode : 1那一行是为了让它在 CI 里能当检查项用返回非零就说明环境有问题。第二个工程验证 JVCL只要窗体上能把配置存储组件实例化并跑起来就行代码放到下一章展开。这里先给一个判断标准窗体设计器里能搜到 JVCL 的控件、拖一个到窗体上不报错、程序能正常启动和关闭三条都满足安装这一步就算过去了。4. 把 JVCL 用起来窗体状态与配置持久化的落地写法4.1 为什么不继续手写 INI手写 INI 的问题不在于难写而在于每次都要重写。窗体位置要存一遍列宽要存一遍上次打开的目录要存一遍每加一个字段就多两行读写代码半年之后没人说得清哪个键对应哪个控件。更麻烦的是类型整数要 IntToStr 再 StrToInt布尔要自己定1/0的约定浮点还要考虑小数点分隔符。JVCL 的配置组件把这件事收敛成一个组件加一条属性。核心是两层存储层负责落到文件INI 或注册表窗体层负责把控件属性按名字映射到存储路径。加了新字段只要在属性字符串里追加一个名字不用改任何读写代码。4.2 一个窗体两个组件三行接上下面这段是完整可编译的窗体单元去掉了.dfm部分只保留代码。实际使用时窗体上不需要放任何组件全部在代码里创建这样窗体的设计文件是干净的。unit UnitSmoke; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.DateUtils, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, JvAppStorage, JvAppIniStorage, JvFormPlacement; type TFormSmoke class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FStorage: TJvAppIniFileStorage; // 存储层负责落盘 FFormStore: TJvFormStorage; // 窗体层负责属性映射 end; var FormSmoke: TFormSmoke; implementation {$R *.dfm} procedure TFormSmoke.FormCreate(Sender: TObject); begin // 1) 存储层Owner 交给窗体生命周期自动托管 FStorage : TJvAppIniFileStorage.Create(Self); FStorage.FileName : ChangeFileExt(Application.ExeName, .ini); // 2) 窗体层绑定存储 FFormStore : TJvFormStorage.Create(Self); FFormStore.AppStorage : FStorage; FFormStore.StoredProps.Text : Width;Height;Left;Top;WindowState; FFormStore.RestoreFormPlacement : True; // 3) 最后再激活顺序反了会读到空配置 FFormStore.Active : True; // 非可视状态路径用反斜杠分层缺失时返回空串 Caption : FStorage.ReadString(App\LastRun, 首次启动); FStorage.WriteString(App\LastRun, DateTimeToStr(Now)); end; procedure TFormSmoke.FormDestroy(Sender: TObject); begin FFormStore.Active : False; // 必须先断开再让 Owner 释放 end; end.逻辑分三步顺序是有讲究的。先建存储层并指定文件名文件名用ChangeFileExt(Application.ExeName, .ini)好处是配置跟着 exe 走绿色部署时不会写到系统目录里去。再建窗体层并绑定存储这一步只是建立引用还没开始读写。最后才把Active置为 True一旦激活组件会立刻去读配置并应用窗体位置。如果把Active : True写到绑定之前读的时候存储还是空的表现就是「第一次启动正常第二次启动位置没恢复」。参数上StoredProps是一串用分号分隔的属性名写在这里的属性会被自动保存和恢复名字必须和控件属性名完全一致大小写敏感。RestoreFormPlacement单独控制位置尺寸这一组和StoredProps里的Left、Top、Width、Height有重叠时以RestoreFormPlacement为准所以我一般只在该属性里写位置不再重复列一遍。FormDestroy里那句FFormStore.Active : False是必须的。窗体销毁时如果存储还处于激活状态它会尝试回写一次而此时部分子控件可能已经先被释放回写过程中访问到已释放的对象就会在退出时抛一个访问违例。这个错只在关闭程序时出现日志里看不出来很容易被当成「随机崩溃」。4.3 什么时候用属性映射什么时候直接读写两种用法对应两种数据。跟着控件走的、和界面强相关的状态用StoredProps或RestoreFormPlacement一行配置解决。和界面无关的业务状态比如上次登录的用户名、上次打开的目录、窗口分割条的比例用存储层的ReadString/WriteString直接读写路径自己分层。数据存放方式建议属性或方法窗体位置、列宽、页签序号控件属性映射StoredProps/RestoreFormPlacement上次打开的目录直接读写ReadString(App\\LastDir, )用户级开关直接读写路径按用户分层ReadBool(User\\ UserName \\AutoSave, True)结构复杂的配置建议自己序列化后整体写入WriteString JSON第四行是我的偏好。JVCL 的存储层支持嵌套路径理论上可以把一棵树展开成很多个键但键一多就难维护改结构时还要写迁移代码。我的做法是把复杂配置序列化成一个字符串整体写进去只占一个键结构变了在代码里加默认值兜底反而更省心。5. 避坑与排查五条踩过的记录5.1 编译期报错现象打开 JVCL 的设计期包直接 Build报E2202 Required package Jcl not found。原因JCL 尚未安装到 IDE或者装了但只是编译没有 Install包列表里没有它。解决回到 JCL 的运行期包确认已经 Build 出.bpl然后在设计期包上右键 Install。装完关掉 IDE 重开一次让包列表刷新再编 JVCL。这个顺序不能省。现象报F1026 File not found: JvTypes.pas路径明明配了。原因只配了 Win32 的 Library Path当前激活的平台是 Win64。IDE 的平台切换不会自动同步库路径。解决在库路径设置里把 Win32 和 Win64 两个节点分别填一遍。填完随手切一次平台再切回来确认两边的路径都在。5.2 运行期异常现象程序关闭时偶发访问违例堆栈指向窗体销毁过程。原因配置存储组件在窗体销毁时仍处于激活状态回写时访问了已释放的子控件。解决在窗体的OnDestroy里先把Active置为 False主动断开。顺序是「先断存储再让 Owner 释放」。现象高 DPI 屏幕上 JVCL 的自绘控件边缘错位、图标发虚。原因老版本控件按 96 DPI 的固定像素绘制与窗体的缩放机制冲突。解决给程序加 DPI 感知声明并把窗体Scaled属性统一设成一致的值不要一部分窗体开、一部分关。混合状态下错位最严重。逐个控件调是下策先统一全局设置再逐个看。5.3 设计期与 IDE 体验现象装完 JVCL 后 IDE 启动明显变慢打开工程时组件面板要转好几秒。原因设计期包把所有控件都注册进面板几百个图标要加载。这是固有成本不是故障。解决只安装实际要用的那几个设计期包用不到的不要 Install。已经装多了的在包列表里卸载掉多余的重启 IDE。还有一条不属于上面三类但很常见把jvcl\resources加进了 Library Path。症状是编译变慢、偶尔报找不到某个资源文件。资源目录不是单元目录它该出现在资源搜索路径里。加错了地方不会立刻报错只是白白拖慢每次编译的速度。6. 进阶把配置层做成可迁移的基础设施单窗体用一个TJvFormStorage就够了项目一大配置就会散在各处A 窗体自己读 INIB 窗体写注册表C 模块直接读环境变量。半年后要改配置目录得全项目搜一遍。我的做法是把存储层抽出来做成一个全局单例所有窗体共享同一个存储对象路径按「域\子域\键」三层划分。function TAppConfig.GetString(const Key, Default: string): string; begin Result : FStorage.ReadString(Key, Default); end; // 调用侧新键没历史值时自动落到默认值不需要版本号迁移 EditUser.Text : AppConfig.GetString(User\LastLogin, ); AppConfig.SetString(User\LastLogin, EditUser.Text);这段封装的关键在第二个参数。ReadString的默认值不是可有可无的装饰它是配置升级的唯一手段。新增一个配置项时老用户的 INI 里没有这个键读出来是空串如果代码直接把空串当有效值用就会出现「升级后某个开关默认关闭」这类问题。把默认值写死在读取处等于给每个新键带了一颗后悔药。验证方法我固定走三步Win32 Debug、Win32 Release、Win64 Release 三个目标各编一遍确认都能链接然后在一台从没装过这套包的机器上只拷 exe 和同目录的 INI 跑一遍确认没有隐式依赖最后把 INI 删掉再启动一次确认所有默认值都能兜住。第三步最容易漏也最容易暴露问题——默认值写错的时候只有配置文件不存在才会显现。血泪经验是有一回交付前只测了开发机客户机器上程序一启动就白屏。查了半天是配置路径里用了当前用户名做分层客户机器的用户名里带点号路径拼出来非法存储组件静默失败窗体位置全成了 0。从那以后我每次改配置路径的拼装逻辑都强制在一台干净机器上删掉配置文件完整跑一遍再换一个带特殊字符的用户名跑第二遍。这个习惯救过不止一次。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站