news 2026/10/23 6:12:44

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Delphi XE11下JCL 2.8/JVCL 3.5安装编译与配置持久化

简介:面向 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 BASE=D:\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 再编 JVCL:run 到 design,最后才是 Install

JVCL 的结构分两层: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 窗体自己读 INI,B 窗体写注册表,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。从那以后我每次改配置路径的拼装逻辑,都强制在一台干净机器上删掉配置文件完整跑一遍,再换一个带特殊字符的用户名跑第二遍。这个习惯救过不止一次。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 6:09:46

【WebSocket】如何处理 WebSocket 的连接关闭事件?客户端和服务端如何优雅地关闭连接?

WebSocket 连接关闭事件处理实战:从协议层到应用层的优雅关闭全链路解析 用户问题原文:如何处理 WebSocket 的连接关闭事件?客户端和服务端如何优雅地关闭连接? 在构建在线客服系统时,我们曾因连接关闭处理不当导致客服状态同步延迟高达 30 秒,用户被错误分配给已离线的客…

作者头像 李华
网站建设 2026/10/23 6:03:42

EeveeSpotify 实测:免费解锁 Spotify Premium 的过程与边界

EeveeSpotify 实测:免费解锁 Spotify Premium 的过程与边界 【免费下载链接】EeveeSpotify A tweak to get Spotify Premium for free, just like Spotilife 项目地址: https://gitcode.com/GitHub_Trending/ee/EeveeSpotify EeveeSpotify 是一款面向越狱 iO…

作者头像 李华
网站建设 2026/10/23 6:00:08

神经网络中的损失函数:常见类型与应用场景(代码演示)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 5:59:08

第一章 技术发展趋势预测:从架构突破到生态重构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华