简介:HTML Component Library v4.6 是一套针对 Delphi 平台的 HTML 渲染控件,主要解决 VCL 与 FMX 程序中嵌入网页、解析 HTML/CSS、处理表单交互等问题,支持从 Delphi 5 到 Delphi 11 Alexandria 的多个版本,适合中高级桌面应用开发者选用。包内共 2000 个文件,压缩后 163.33MB,包含 564 个 dcu 编译单元、500 个 hpp 头文件、190 个 obj 文件等核心构建产物;同时带有 dfm 窗体、fmx/lfm 跨平台界面文件,以及 dpk/dproj 工程和 bpl/dcp 运行时包,便于按目标 IDE 快速安装部署。另外还提供 chm、pdf 文档与示例工程,方便查阅接口和上手测试。已有 54 人学习/下载。同时附带授权补丁与 pas 源文件,既能直接集成到开发环境,也可基于源码对组件行为进行定制,有助于缩短 HTML 相关功能的开发周期。
1. Delphi 12.3 上使用 HTML Component Library v4.6,先想清楚它到底解决什么
在 Delphi 12.3 的 IDE 里,HTML Component Library v4.6 是一套老牌第三方控件,提供 THtmlViewer、TFrameViewer 等可直接拖放的 HTML 渲染组件,同时覆盖 VCL 和 FMX,版本跨度从 D5 一直到 D11 Alexandria。很多做 ERP、进销存、报表系统的工程师看到“HTML 控件”,第一反应是挂一个 WebBrowser 或 EdgeBrowser,但 HCL 的做法完全不同:它用自己的解析器和排版引擎画 HTML,不依赖操作系统里的浏览器进程,因此安装体积小、启动快,也几乎没有浏览器版本“昨天能跑今天不能跑”的问题。对正在学习 Delphi 的开发者,以及用 Community Edition 做免费桌面工具的团队来说,这套控件比 CEF 更容易上手,比 WebBrowser 更容易控制渲染结果。本文只讨论 v4.6 的安装、VCL/FMX 接入、参数调优、常见坑和 HTTP 链接拦截技巧;标题里出现的 CRACK 文件不属于开发技术内容,请从正式授权渠道获取组件安装包,后面每个步骤都只针对正规安装包。
2. HTML Component Library v4.6 的架构与选型:为什么它比 Edge/WebView2 更可控
HCL v4.6 最容易被误判的一点是“它到底是不是一个浏览器”。它既不是 Chrome 也不是 IE 的封装,而是一个用 Delphi 对象写出来的 HTML 渲染引擎。理解这一点,才能解释为什么有的人在老电脑上飞一样运行,有的人一接图片多的页面就卡,也才能决定你的项目里哪些页面适合交给它。
2.1 自绘内核与浏览器内核的差异
HCL 在 VCL 版本里通过 GDI 绘制 HTML 元素,在 FMX 版本里则走 TCanvas 绘图栈。它保存的是一棵元素节点树,然后自己决定字体、边框、段落和表格怎么排。这意味着它不启动子进程、不占用 GPU、不要求系统安装 WebView2 Runtime,也没有 ActiveX 注册表残留。代价是它对 JavaScript 的支持很弱,对 CSS3 布局也只能覆盖一部分。拿它渲染完整版的单页后台系统会吃力,拿它渲染订单、发票、邮件模板、帮助文档则非常合适。
下面这张表是我通常会拿给项目组做选型对比的。
| 对比项 | HCL v4.6 | WebView2 | CEF |
|---|---|---|---|
| 内核来源 | Delphi 自写 | Edge Chromium | Chromium |
| 进程模型 | 单进程 | 多进程 | 多进程 |
| 安装依赖 | 无 | 需要 Runtime | 需要大量 DLL |
| VCL/FMX 原生支持 | 两者都有 | 需要第三方封装 | 需要第三方封装 |
| HTML5/CSS3 | 可用但有限 | 完整 | 完整 |
| 内存占用 | 较低 | 较高 | 最高 |
| 离线场景 | 优秀 | 依赖缓存 | 依赖缓存 |
选型时我给的最实用建议是:如果页面固定为项目里自己生成的 HTML,且不需要现代前端框架,HCL 是成本最低的方案;如果页面可能是外部页面、会频繁升级,甚至要用 React 或 Vue,那就别在 HCL 上浪费力气,直接上 WebView2 或 CEF。怕就怕抱着“HCL 能渲染 HTML 就等于能替代浏览器”的预期,最终做到一半发现撑不住。
2.2 VCL 与 FMX 两套分支的设计差异
v4.6 对 VCL 和 FMX 分别维护了单元包,而不是用同一套代码硬兼容。VCL 分支依赖 WinAPI 的句柄和消息传递,所以在 Windows 下控件交互跟原生控件一样自然;FMX 分支则必须用 FMX 的跨平台 TCanvas 去画 Text、Rect 和图片,因此在 Linux、macOS 甚至 Android 上也可以显示 HTML 页面,只是字体获取方式和输入法行为不同。这就带来一个常见的工程结论:老项目升级、保留 Windows 特有能力时优先 VCL;如果是新项目并且明确有多端交付要求,FMX 分支才是正确选择。两者的公开 API 高度接近,但不要在 DFM 里直接混拖 VCL 和 FMX 版本的组件,一个窗体只能使用同一套 UI 框架。
2.3 为什么 D5 到 D11 Alexandria 都要兼容
v4.6 能在这么长的 Delphi 版本区间里通用,核心原因是包内做了大量条件编译。它不是分别维护六七个源码副本,而是在同一个源文件里用编译器版本指令切分差异代码。老项目从 Delphi 7 升到 Delphi 12.3,最常见的工作量不在 HCL,而在你自己的字符串类型和 Windows API 调用。HCL 提供的公共组件通常只依赖很少的底层单元,所以升级时可以直接重用旧代码里的LoadFromString、OnHotSpotClick这些逻辑。
这里也留一个给 Delphi 入门者看的代码片段:在单元头部按编译器版本做初始化,可以避免在旧版本上误用新语法。
unit HtmlInit; interface procedure InitHCLEnvironment; implementation procedure InitHCLEnvironment; begin {$IF CompilerVersion >= 35.0} // Delphi 11 Alexandria 以上才需要执行的额外初始化 {$IFEND} end; end.这段代码里CompilerVersion是编译器内置的浮点常量,35.0 大致对应 Delphi 11 Alexandria。v4.6 源码里遍布类似的$IF分支,说明它的设计目标就是让同一份用户代码尽量不经修改跨版本编译。我一般在升级前会先确认项目里有没有直接引用 HCL 私有单元,比如从HtmlView内部翻出一些没公开的变量;只要公共 API 用法规范,D5 时代的代码放到 Delphi 12.3 里仍然能编译通过。
3. 在 Delphi 12.3 中安装并注册 HCL v4.6 组件包
安装 HCL v4.6 的流程和装普通第三方控件包基本一致,但因为它是老式组件库,目录里可能同时有 D5、D7、XE、D10、D11 多套包文件,不少人就是栽在“打开一个 .dpk 就编译,结果报版本不匹配”。所以要先看包内目录,再按 Delphi 12.3 对应的包文件走。
3.1 先确认包内目录结构
一个规范发布的 v4.6 包通常包含 source、packages、bin 三类内容。拿到压缩包后,不要先着急双击安装,先看目录结构。
| 目录/文件 | 作用 |
|---|---|
| Source/VCL | VCL 版控件源码,加入 Library Path 后编译期才能找到单元 |
| Source/FMX | FMX 版控件源码 |
| Packages/D5-D11 | 各版本.dpk/.bpl工程文件 |
| Bin | 预编译的 DCU 或包输出 |
| Demo | 示例工程,安装后对照使用 |
如果只有源码而没有现成的编译好的包,需要自己在当前 IDE 环境中编译一次。如果包里带有为 Delphi 12.3 单独准备的包文件,直接打开安装。
3.2 设置 Library Path 并编译安装
在 Delphi 12.3 中,先打开Tools > Options > Environment Options > Delphi Options > Library - Win32,把Source\VCL和Source\FMX加入 Library Path。这一步不能跳过,因为.dpk编译时只会按项目搜索路径找HtmlViewer、Htmlsbs这些基础单元。
接下来在主菜单选择Component > Install Packages,或者直接打开 Packages 目录下的.dpk文件。一个典型操作顺序是:
- 在 Project Manager 里找到对应 Delphi 12.3 的 VCL 包,例如
D12_HCL_VCL.dpk - 右键选择
Compile - 编译成功后右键选择
Install - 看到 “Package ... installed” 提示,组件会自动出现在 Tool Palette
- 用同样的方法编译并安装 FMX 包
如果不是源码安装,而是已经给您提供了.bpl文件,则在Install Packages对话框中点击Add,选择 VCL 和 FMX 两个.bpl即可。安装完成后,在工具面板搜索FrameViewer,能出现对应图标就算成功。
3.3 用一个最小代码验证安装环境
安装未必等于编译环境可用。Library Path 漏配是新手最常见的坑,表现为打开示例工程时提示找不到HtmlViewer单元。这里给一个独立的验证函数,放在一个空单元里,只测试单元的编译期查找是否正常。
uses HtmlViewer; function HCLCanCreate: string; var V: TFrameViewer; begin V := TFrameViewer.Create(nil); try Result := 'OK: ' + V.ClassName; finally V.Free; end; end;这个函数能编译通过,说明 Delphi 12.3 的 Library Path 已经正确指向 HCL 源码;调用它并返回OK: TFrameViewer,则说明运行时也会创建组件对象。要注意的是,TFrameViewer.Create(nil)不会自动设置 Parent,也不会显示界面,因此在这个验证函数里不需要窗口句柄。万一编译失败,优先检查Source\VCL是否被加入当前平台为 Win32 的路径里,而不是 Win64 或其他平台。
4. 在 VCL 与 FMX 中嵌入 HTML 的实战配置
安装成功之后,最理想的用法不是把 HTML 塞进 TWebBrowser,而是直接把TFrameViewer或THtmlViewer当作普通控件放到窗体上。下面从 VCL 和 FMX 两个最小工程讲起。
4.1 VCL 最小示例:LoadFromString 显示报表
在 VCL 窗体上放一个 TFrameViewer(IDE 拖拽后默认名是 FrameViewer1),再放一个 TButton。按钮事件里写:
procedure TForm1.ButtonLoadClick(Sender: TObject); begin FrameViewer1.Align := alClient; FrameViewer1.DefFontName := 'Microsoft YaHei'; FrameViewer1.DefFontSize := 10; FrameViewer1.LoadFromString( '<html>' + '<head><meta charset="utf-8"></head>' + '<body>' + '<h2>Delphi 12.3 + HCL v4.6</h2>' + '<p>报表正文区域</p>' + '</body>' + '</html>'); end;LoadFromString的参数是 Delphi Unicode 字符串,HCL 会直接解析内存中的 HTML,不会发起网络请求。DefFontName和DefFontSize决定了 HTML 里没有显式写字体样式的那些文字怎么显示。Windows 环境下把默认字体设置为 Microsoft YaHei,中文显示效果会比较稳定;如果完全不设置,组件通常沿用 Windows 的系统 UI 字体,字号偏旧。
这里有一个实战中容易踩的误区:不要在运行时反复设置FrameViewer1.Align。如果窗体设计时就已经设置了 Align,运行时再改意义不大,而且可能触发布局刷新。上面这个写法只是为了在完全手写代码的工程里也能跑起来。
4.2 FMX 最小示例:跨平台输出 Hello HTML
FMX 版本的组件在 Delphi 12.3 中安装后,会出现在 FMX 组件页里。假设在 Form 上拖拽后 IDE 生成的控件名是HtmlViewer1,按钮事件可以这样写:
procedure TForm2.Button1Click(Sender: TObject); begin HtmlViewer1.LoadFromString( '<html>' + '<body>' + '<h2>FMX Hello</h2>' + '<p>HTML Component Library v4.6 在 FMX 下同样可用。</p>' + '</body>' + '</html>'); end;FMX 版不依赖 Windows GDI,因此在 macOS 或 Android 上也能绘制出 HTML 页面。但要注意,移动端的默认字体列表和 PC 完全不同。如果在 Android 上显示中文出现方块,不要急着怀疑 HCL,先检查 Delphi 工程是否启用了自定义字体,或者是否在 FMX 表单的OnCreate里设置了FontManager的字体路径。常见的做法是给 DefFontName 传入平台自带的中文字体名,VCL 用微软雅黑,macOS 用 PingFang SC,Android 用 Droid Sans Fallback 或 sans-serif。
4.3 常用属性、方法和事件速查
初用 HCL 时,掌握几个高频入口就够了。下面的表是基于我实际使用 v4.6 VCL 分支的经验整理出来的,FMX 分支的命名基本一致,但个别属性可能由 IDE 自动生成。
| 成员 | 类型 | 作用与建议 |
|---|---|---|
| DefFontName | 属性 | 设置默认字体,例如 'Microsoft YaHei' |
| DefFontSize | 属性 | 默认字号,建议 9 到 12 |
| MarginWidth | 属性 | 页面左右边距,单位像素 |
| MarginHeight | 属性 | 页面上下边距 |
| LoadFromString | 方法 | 从字符串加载 HTML,适合报表模板 |
| LoadFromFile | 方法 | 从文件加载 HTML,适合离线文档 |
| OnHotSpotClick | 事件 | 链接被点击时触发,可拦截并自行处理 |
| OnLinkClick | 事件 | 某些版本中由 OnHotSpotClick 转发,用于外部浏览器 |
LoadFromFile加载本地 HTML 文件时,路径分隔符建议用绝对路径或相对于ExtractFilePath(Application.ExeName)的路径,避免把相对路径写死在当前目录里。遇到外链时,HCL 默认可能会打开系统默认浏览器;如果希望链接永远不跳转,只需要在事件处理函数里把Handled设为 True。这个机制是后面做自定义协议桥接的基础。
5. 中文乱码、EdgeBrowser 无反应与性能排错
任何 HTML 渲染组件都绕不开字符集问题,HCL v4.6 尤其明显。它不是一个现代浏览器,不会自动嗅探编码,也不会因为你在 HTML 里写了<meta charset="utf-8">就一定不出乱码。乱码的根源通常是 Delphi 字符串和 HTML 字节流之间的编码转换没有做对。
5.1 中文乱码:不只是 HTML 声明问题
我见过很多 Delphi 新人把 UTF-8 的 HTML 文件直接交给LoadFromFile,然后在头部写了 charset,结果中文还是乱码。原因是 Delphi 在读取文件时默认按本地代码页(Windows 中文系统为 GBK)解析字节流,如果文件本身是 UTF-8 无 BOM,premature 转码就会出错。正确做法是先按 UTF-8 解码字节,再交给组件。
uses System.SysUtils, System.NetEncoding; function LoadUtf8Html(const FileName: string): string; var Bytes: TBytes; begin Bytes := TFile.ReadAllBytes(FileName); Result := TEncoding.UTF8.GetString(Bytes); end; // 调用 FrameViewer1.LoadFromString(LoadUtf8Html('report.html'));逻辑说明:TFile.ReadAllBytes读到的是原始字节,TEncoding.UTF8.GetString明确按 UTF-8 解码成 Unicode 字符串。这个做法比让 HCL 自己读文件可靠,因为不依赖组件的内部编码判断。如果是数据库字段,比如从 SQLite 里读出来的中文备注,也是一样的原理:先拿到字段对应的 AnsiBytes 或 Blob,再统一转成 UTF8 字符串后拼接 HTML。很多人碰到的“Delphi SQLite 乱码”,本质就是 CSV 或 Blob 里存的是 GBK,而拼接进 HTML 后却按 UTF-8 解码。
5.2 别用 EdgeBrowser.Navigate 来做静态报表
有个词在最近的问题里出现频率很高:“Delphi 运行 EdgeBrowser.Navigate 无反应”。这通常是 Edge WebView2 控件在窗口句柄尚未创建、或浏览器进程初始化失败时导致的。如果你只是在做报表打印和邮件模板,完全可以不碰 EdgeBrowser。HCL 自绘引擎没有独立进程,不存在“导航无反应”这种状态,因为它根本不会导航。它只解析你给它的 HTML 字符串。如果给了完整 URL 必须联网,HCL 的能力很弱,这也不是它的设计目标。
反过来,如果确实要用 EdgeBrowser 展示在线页面,排错顺序应该是:先确认 WebView2 Runtime 已安装,然后在FormCreate里调用EdgeBrowser1.CreateBrowser,再在OnNavigationCompleted事件中调用Navigate,不要直接在FormCreate里写一句Navigate('https://...')就完事。HCL 和 EdgeBrowser 的适用场景是互补的,选择依据是页面里到底有没有不可缺失的 JavaScript。
5.3 大表格与图片资源的性能控制
HCL 为每个 HTML 元素保留对象节点,一个大表格如果超过几千行,内存会明显上涨。我能给的最直接建议是:不要把整张数据库表直接铺到 HTML 里。分页、懒加载、HTTP 图片链接可以缓解,但最有效的是拆分页面。比如把每一页报表限制在 500 行以内,连续查看时先Free旧的 FrameViewer,再创建新实例。以下代码适合在反复加载报表时用:
procedure TForm1.ReleaseViewer; begin FrameViewer1.Stop; // 停止当前解析 FrameViewer1.Clear; // 清空页内容 FrameViewer1.Free; FrameViewer1 := nil; end;Stop和Clear是 v4.6 常见生命周期方法,前者在图片还没下载完时中止解析,后者清空节点树。如果组件是在设计期拖上去的,直接 Free 会在关闭窗体时造成二次释放,所以这里要把FrameViewer1 := nil。比较稳妥的工程做法是运行时动态创建,而不是设计期摆死。
5.4 跨版本编译注意点
虽然 v4.6 支持 D5 到 D11 Alexandria,但你在 Delphi 12.3 里写代码时要小心新语法漏进老版本分支。比如Inline Var、泛型匿名函数只适合新版本。如果有需要交付给 D5 环境的包,建议用$IF CompilerVersion把新语法隔开。另一个常见问题是字符串索引从 1 开始还是 0 开始,这在老版和新版 Delphi 里行为不同,处理 HTML 字符串时尽量用Copy、Pos这类跨版本统一函数。
6. 用 hcl:// 自定义协议把 HTML 点击事件桥接到 Delphi 后端
安装和基本渲染跑通之后,一个很实用的能力是让 HTML 页面里的链接直接调用 Delphi 方法。最可靠、不依赖 JS 引擎的方式是拦截 OnHotSpotClick 事件,识别hcl://协议头。理解起来像浏览器里的自定义 URI Scheme,只是这里完全由 Delphi 自己处理。
先构造一个 HTML 示例,放在按钮点击事件里加载:
procedure TForm1.ButtonLoadClick(Sender: TObject); begin FrameViewer1.LoadFromString( '<html><body>' + '<h3>自定义协议测试</h3>' + '<a href="hcl://export">导出当前报表</a><br>' + '<a href="hcl://close">关闭窗口</a>' + '</body></html>'); end;然后在 FrameViewer1 的OnHotSpotClick事件里写:
procedure TForm1.FrameViewer1HotSpotClick(Sender: TObject; const SRC: string; var Handled: Boolean); var Action: string; begin if Pos('hcl://', LowerCase(SRC)) = 1 then begin Handled := True; Action := LowerCase(Copy(SRC, 7, MaxInt)); if Action = 'export' then ExportReport else if Action = 'close' then Close; end; end;SRC参数是 HTML 中 href 的原始字符串。Handled := True表示这个链接已经被 Delphi 消费,HCL 不会再尝试打开系统浏览器。Copy(SRC, 7, MaxInt)用来去掉hcl://这部分前缀,留下export或close。这里强制转小写,是为了避免用户在 HTML 里写HCL://EXPORT导致协议判断不一致。
这个模式的进阶价值在于:业务侧同事可以独立写 HTML 文档,只需要按规定好的协议名称拼链接,不需要接触 Delphi 代码。对风险控制也有一个直接好处:白名单机制很简单,只放行export、close这些固定动作,未识别的协议一律Handled := False,让外部浏览器接管。如果协议参数需要传值,比如hcl://show?id=100,建议再做一层 URL 解码,把&还原成&,再把参数交给专门的数据访问单元。这样前端模板和后端逻辑彻底分离,是 HCL 在小团队里最实用的架构技巧。
本文还有配套的精品资源,点击获取