news 2026/9/3 1:31:06

CEF4Delphi实战:Delphi桌面应用嵌入Chromium内核浏览器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CEF4Delphi实战:Delphi桌面应用嵌入Chromium内核浏览器

简介:CEF4Delphi 86.0.23.0版组件包是面向Delphi和Lazarus/FPC开发者的开源集成方案,用于在桌面应用中嵌入Chromium内核。压缩包共1383个文件,以pas源码、dcu编译单元、dproj工程文件、inc头文件及dll运行库为主,并附带win32/win64支持包,包体约143.74MB,可配合Delphi开发环境直接安装使用。已有1282人学习下载。内容涵盖完整项目源码、多个示例工程、资源文件与各平台配置,适合从入门到进阶的开发者参考或二次开发,有助于快速搭建CEF环境、理解组件封装结构,并减少环境配置与调试成本。 做Delphi桌面开发这些年,遇到需要内嵌网页功能的需求次数,远比我预想的多。菜单页要上HTML5大屏,报表要出ECharts图表,系统帮助文档得塞进本地Web页面,更别说那些必须嵌第三方OAuth登录页的场景。Delphi自带的TWebBrowser控件基于老的IE内核,打开现代页面要么白屏要么缺功能,最终几乎所有人都绕到CEF4Delphi这条路上。这次分享的CEF4Delphi 86.0.23.0版,是带支持库的完整发布包,解压就能在Delphi工程里跑起Chromium 86内核的嵌入式浏览器,适合VCL项目上需要稳定内嵌Web前端的开发者参考。下面从版本解析、配置、代码骨架和实际踩坑四个方面展开,尽量把我自己的使用体验说清楚。

1. 为什么Delphi项目需要CEF4Delphi:从IE控件到Chromium内核

1.1 CEF4Delphi是什么,它到底做了什么

先讲清楚底层逻辑。CEF(Chromium Embedded Framework)是一个开源的内嵌浏览器框架,把Chromium浏览器核心封装成一套稳定的C API,让原生应用可以在自己的窗口里渲染网页、处理JS、管理Cookie和缓存。CEF4Delphi就是这套C API在Delphi下的封装层,把整个浏览器跑起来需要的初始化、多进程调度、事件回调都包装成VCL组件,开发时直接拖一个TChromium到窗体上就能用,不需要手动维护底层状态机。

这个封装不是简单包装一个DLL调用,而是把CEF的多进程模型、消息循环、资源加载都整合进了Delphi的Application消息循环。比如CEF要求主进程和子进程互相协作,CEF4Delphi会在应用启动时用GlobalCEFApp去初始化,再通过StartMainProcess统一处理主进程和子进程的分支。原理上理解清楚这一点,后面排查白屏、崩溃类问题会轻松很多。

1.2 三类典型的嵌入式浏览器应用场景

从我接触过的项目来看,CEF4Delphi的应用场景基本能归成三类。

第一类是“Web UI + 原生壳”模式。界面用HTML/CSS/JS来写,Delphi只负责窗口框架、本地文件读写、系统硬件调用。这种组合在数据大屏、报表可视化项目里特别多,一套ECharts图表页面可以直接跑,不用再调一个WinForms或WebView包装器。

第二类是本地离线文档或资料浏览器。把帮助手册、产品说明打包成HTML资源,程序里用file协议加载,比维护CHM或者自带PDF查看器省事得多。CEF4Delphi对file协议、本地存储的支持很完整,目录结构做对就能稳定加载。

第三类是集成第三方Web服务。OAuth授权登录、在线支付页面、SaaS平台的嵌入页面,这些场景要求浏览器内核不能太旧,否则HTML5的API、JSON解析、新的SSL配置都可能出问题。CEF4Delphi的Chromium内核版本跟随更新,天然比IE内核的TWebBrowser靠谱。

我早期有个项目用的TWebBrowser,客户反映页面按钮点不动,查下来是页面里用了ES6的Promise、async/await,老IE内核直接解析失败。换成CEF4Delphi之后这种兼容性问题基本消失,因为Chromium 86对现代Web标准的支持已经是完整水平,Promise、fetch、WebSocket、WebGL、WebAssembly都能跑,大部分客户端Web需求不用再额外做降级方案。

说一句大实话,虽然CEF4Delphi新版本已经更新到更新内核,但86.0.23.0这个版本在今天不少生产环境里仍然能打,原因在于它在功能完整度和资源占用之间平衡得比较好,比Chromium 90+的版本内存占用明显低,开一个嵌入式页面大概多个几十MB,对于传统的桌面客户端来说完全能接受。另外CEF4Delphi是开源免费的,遵循LGPL相关的使用条款,闭源商用项目使用时建议仔细查看对应版本的LICENSE说明,避免后续授权踩线。

2. 86.0.23.0版本解析:版本号逻辑和“带支持库”的含义

2.1 版本号到底代表什么

CEF4Delphi的版本号是跟随CEF版本走的。86.0.23.0这4段里,86对应Chromium 86大版本,0.23是CEF的补丁维护版本,最后一个0是CEF4Delphi封装层的修复版本。也就是说,86.0.23.0这个版本是基于Chromium 86内核做的封装,不是“第86次发布”这种意思。理解这个命名规则很重要,因为很多新手会把CEF4Delphi的版本号当成自己的发布迭代,看到86就觉得“版本太高了”,其实86只是Chromium的大版本号。

Chromium 86大概在2020年10月发布,支持完整的ES2015到ES2020大部分特性、WebGL2、WebAssembly、Service Worker等关键能力。如果项目里需要加载的网页不太新,86足够稳定;如果页面依赖了2022年以后的浏览器新特性,比如某些新的CSS容器查询或者最新的Array方法,那就得考虑升级到更新内核的版本。这点在选择版本时一定要先验证目标页面在Chromium 86上的兼容性,不要拿到手闷头开发到最后才发现核心页面渲染不出来。

2.2 “带支持库”的目录结构

“带支持库”意味着发布包中已经包含了CEF运行所需的完整二进制文件,解压后无需再去CEF官方下载对应版本的binaries。这一步省掉的是最常见的版本错配坑,因为手工下载时如果误用了CEF 85或87的二进制,运行时大概率会出现莫名其妙的崩溃、加载失败,排查起来很痛苦。

支持库目录里这些文件都有明确的作用,按经验删不得:

  • libcef.dll:核心引擎,整个Chromium都在里面,体积最大,少了它程序直接启动失败。
  • icudtl.dat:ICU的Unicode数据处理文件,负责字符集、国际化、字符串处理,删了会出现乱码和崩溃。
  • v8_context_snapshot.bin和snapshot_blob.bin:V8 JavaScript引擎的启动快照,加速页面脚本初始化。
  • locales目录:各语言包资源。
  • swiftshader目录:软渲染实现,GPU不可用时兜底渲染。
  • resources目录:CEF自带的Web资源,比如错误页、内置UI。

举个例子,老有人觉得“我页面只有中文,locales里只留zh-CN.pak好省体积”,实测删掉英文语言包后,有些地址栏输入、右键菜单的默认行为会回退异常,甚至触发CEF的断言。所以包里带什么就保留什么,不要动。

实际部署时,这些文件必须跟exe在同一目录,或者通过WorkingDir等参数指定正确路径,否则CEF初始化时找不到libcef.dll就直接退出。我在使用时习惯把支持库文件以文件拷贝方式放到发布目录,避免被IDE的build行为覆盖或者漏拷。

3. 从下载到跑通示例:完整配置流程

3.1 下载、解压和目录规划

拿到86.0.23.0带支持库发布包后,先解压到一个目录。我通常建议不要解压到带有空格和中文的路径下,虽然CEF4Delphi对长路径容忍度比早期好一些,但工具链和编译期仍可能出现不可预期的路径问题。我见过有人把项目放到“E:\我的项目\客户端”下,编译一切正常,但运行时CEF的子进程起不来,后来放到纯英文路径就稳定了。

目录规划建议是:

D:\CEF\cef4delphi\ 源码工程目录 D:\CEF\cef4delphi\cef\ 支持库和CEF二进制目录 D:\CEF\myproject\ 你自己的工程目录

自带Demo工程可以直接在cef4delphi目录下打开,但真正做项目时建议把源码目录视为只读引用,单独建工程,避免升级组件时把业务代码弄乱。我自己的习惯是:源码和Demo单独放一个开发目录,业务工程放到另一个位置,用Library Path引用组件源码,这样升级组件版本时不用动业务工程结构。

3.2 Delphi环境设置

打开Delphi IDE后,在Tools > Options > Language > Delphi > Library中,把源码目录下的Source路径加入Library Path。CEF4Delphi的源码组织方式一般是根目录下的Source和第三方依赖目录都要加。如果不加,打开Demo工程时会有大量找不到单元的报错,因为很多公共单元被设计成通过Library Path引用而不是直接放到每个工程里。

不同Delphi版本的Library Path配置界面有一点差异,但核心都是把“目录”加入搜索路径。另外确认你的Delphi版本在CEF4Delphi支持范围内,86.0.23.0官方支持Delphi 10.2、10.3、10.4这些常见版本,新一点的Delphi 11、12也能用,但如果遇到编译期不兼容,优先检查是否缺了官方要求的条件编译符号。

我实际使用中会再建一个仅包含当前项目相关单元的顺序列表,因为Library Path虽然能解决单元搜索,但多个版本的CEF4Delphi如果同时存在于Library Path里,IDE会随机命中一个,编译出来就不是你预期的API了。这个问题在笔记本上装了多个Delphi版本的开发环境里尤其常见。

3.3 编译并运行第一个Demo

官方Demo很多,最推荐先跑SimpleBrowser。打开dpr后直接编译,如果编译通过,运行目录下会自动带出支持库文件——前提是输出目录和cef二进制路径配置正确。CEF4Delphi的工程里一般会做后置构建事件或者引用文件拷贝,把cef目录里的支持库复制到输出目录。

第一次运行时如果闪退,多半是支持库没在exe旁边。打开Windows任务管理器如果看不到进程,直接去exe所在目录检查libcef.dll、icudtl.dat等文件是否存在。这一步排查其实很简单:把发布包里cef目录下的所有文件先完整拷到输出目录,再运行,基本能解决。

跑通SimpleBrowser后可以打开SimpleBrowser2,它演示了更完整的页面导航、调试模式、JS执行等交互能力。这个Demo很适合作为新工程的基础模板,我在实际项目中经常直接复制SimpleBrowser2的工程结构调整,能少踩不少坑,尤其是它把初始化参数、缓存目录、日志开关都已经写好了,改一改就能用。

4. 接入真实项目的核心代码骨架

4.1 在工程文件里初始化CEF

跑通Demo后接入自己的工程,第一步是配置dpr。在Application.CreateForm之前,必须先创建并启动CEF。我项目的dpr核心代码大概是这样的:

program MyClient; uses System.SysUtils, System.IOUtils, Vcl.Forms, uMainForm in 'uMainForm.pas', uCEFApplication in 'uCEFApplication.pas'; {$R *.res} begin PlatformReportMemoryLeaksOnShutdown := False; GlobalCEFApp := TCefApplication.Create; try GlobalCEFApp.WorkingDir := ExtractFilePath(Application.ExeName); GlobalCEFApp.RootCacheDir := ExtractFilePath(Application.ExeName) + 'CEFCache'; GlobalCEFApp.Cache := ExtractFilePath(Application.ExeName) + 'CEFCache'; GlobalCEFApp.EnableHighDPISupport := True; GlobalCEFApp.StartMainProcess; if GlobalCEFApp.ProcessType = ptBrowser then begin Application.Initialize; Application.CreateForm(TMainForm, MainForm); Application.Run; end; finally GlobalCEFApp.Free; end; end.

注意StartMainProcess这个调用是整个初始化中最核心的部分。它做的事情不是简单的启动服务,而是判断当前进程是主浏览器进程还是CEF派生的子进程:如果是子进程,它会直接进入CEF的子进程消息循环,不再执行后面的Application.Run;只有主浏览器进程才会继续出来创建窗体。所以判断用ProcessType = ptBrowser来控制是否创建主窗体,比单纯看返回布尔值更严谨。这个细节我抄了很多例程都没写,因为在多进程模型下子进程也会进入dpr入口,如果你不拦一下,程序可能开出一堆窗口。

WorkingDir必须设置成exe所在目录,否则支持库查找可能失败。RootCacheDir和Cache指定了浏览器缓存位置,我建议在自己项目里给一个独立文件夹,方便升级版本时清理缓存,避免旧缓存影响新页面显示。日志方面,如果不需要调试,尽量关闭GlobalCEFApp.LogFile和相关的详细日志,否则运行一段时间后日志文件会膨胀得很快。

4.2 用TChromium加载页面

初始化完成之后,窗体内放置一个TChromium组件,核心设置如下:

begin Chromium1.DefaultURL := 'https://example.com'; Chromium1.Align := alClient; Chromium1.Parent := PanelMain; Chromium1.CreateBrowser; end;

把DefaultURL设置好,CreateBrowser之后组件会自动加载页面。TChromium封装了很多事件,常用的有OnLoadEnd、OnLoadError、OnBeforeBrowse、OnCertificateError等。我做项目时一般会在OnLoadEnd里做页面就绪判断,OnLoadError里做加载失败提示,这两个事件对用户感知影响最大,是排查问题的一线入口。

如果你不想手动管理嵌入窗口的尺寸和位置,也可以直接使用TChromiumWindow。这个类把TChromium和承载它的窗口容器封装在一起,窗体上放一个TChromiumWindow,设置好DefaultURL,运行时它会自己处理窗体嵌入。SimpleBrowser3和SimpleBrowser4两个Demo演示的就是这种用法,代码量能少三分之一,适合快速搭原型。

还有一点:TChromium本身不是TWinControl标准的可视化控件,它内部是CEF提供的原生窗口,所以Align、Parent这些属性虽然可以用,但在窗体Resize、Dock操作特别频繁时,偶尔会出现渲染画面和窗体不同步的问题。实际项目中我会在FormResize后主动延迟刷新一下,或者调用与之相关的大小通知方法(具体名称以当前版本API为准),解决画面撕裂或空白区域的问题。

4.3 与网页做JS双向通信

浏览器组件最难的部分通常是和页面的数据交互。CEF4Delphi实现JS通信有几种常用方式。第一种是在页面加载时注册JS扩展,让网页可以调用原生Delphi函数,但实现起来相对重,需要理解CEF的扩展注册机制。

第二种方式是拦截导航请求,用自定义协议跳转。这个方式很实用,不依赖完整的JS Binding注册:

procedure TMainForm.Chromium1BeforeBrowse(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest; user_gesture, is_redirect: Boolean; out Result: Boolean); begin if Pos('myapp://', request.Url) > 0 then begin DoNativeAction(request.Url); Result := True; // 阻止页面跳转 end; end;

页面里点击某个按钮,用location.href='myapp://openfile?path=xxx'触发,Delphi侧拦截后调用本地功能。这种方式实现简单,逻辑清晰,出问题也容易跟踪,是我在多个项目里首选的通信方案。

第三种方式是执行页面里的JavaScript。通过Chromium1.Browser.MainFrame.ExecuteJavaScript('alert(1);', '', 0),可以在页面上下文里执行任意JS,适合把Delphi数据推给页面。如果只是做页面和原生数据的双向同步,我建议自定义协议和ExecuteJavaScript组合使用,够用且维护成本低。完整JS绑定虽然看起来很酷,但在86.0.23.0这个版本里写起来绕,调试也不够直观,除非你有大量高频数据交互,否则不建议一上来就用。

5. 实测中容易踩的坑和避坑经验

5.1 白屏问题

CEF4Delphi用起来最常见的现象就是窗口出来了,里面一片白。这个问题的原因通常分三类。

第一类是GPU渲染问题。Chromium默认启用GPU加速,在虚拟机、远程桌面、老旧显卡驱动的机器上,GPU进程可能起不来导致白屏。遇到这种情况,在初始化时关掉GPU加速:

GlobalCEFApp.EnableGPU := False;

重新编译运行,绝大多数白屏能解决。代价是页面滚动、动画的流畅度会降一些,但稳定性优先。我甚至遇到过一个金融客户的办公电脑,集显驱动有问题,只要开了GPU必白屏,关掉之后一切正常。

第二类是支持库缺失或路径不对。libcef.dll、icudtl.dat、v8_context_snapshot.bin这些文件只要少一个,页面就可能静默白屏而不是报错。检查exe目录下文件是否齐全,注意发布时别用压缩工具漏打包。第三类是Windows的缩放比例问题。在DPI缩放较高的屏幕上,嵌入式页面渲染区域会出现大片空白。设置EnableHighDPISupport之后,大部分高分屏问题会缓解,但个别机器仍可能出现渲染区域计算错误,这时需要检查窗体创建和CEF浏览器创建时的尺寸同步。

5.2 32位/64位架构和支持库错配

支持库的架构必须和exe的编译目标一致。86.0.23.0版本同时提供了win32和x64的CEF二进制,如果你的Delphi工程编译成32位,而libcef.dll是64位,程序一启动就会报“Cannot load libcef.dll”或者直接崩溃。这个坑看着低级,但项目换电脑、换编译配置后很容易踩到,因为IDE的默认Target Platform可能被改掉。

检查方法很直接:在Windows资源管理器里看exe架构,或者在任务管理器的详细信息页确认进程是32位还是64位;然后确认libcef.dll的架构。Delphi工程编译目标也要同步核对,IDE右下角或Project Manager里的Target Platforms不要选错。我在项目里会单独建Release和Debug两个编译配置,分别锁定32位/64位目标,避免手动切换时出错。

5.3 多进程架构带来的进程管理

CEF是多进程架构,一个主进程会衍生出GPU进程、Renderer渲染进程、网络进程等一堆子进程。正常使用时这没问题,但如果你在任务管理器里看到十几个同名exe进程,不要以为是病毒或内存泄漏,这是CEF的正常表现,每个渲染标签页或iframe都可能对应独立的渲染进程。

这个架构带来的一个实际问题:如果你用了全局异常处理或者进程守护逻辑,子进程崩溃时会触发某些钩子,可能导致程序误退出。我给项目的建议是在主进程里收集异常,但不要在子进程里做复杂的异常上报,CEF自己的崩溃恢复机制会处理渲染进程的崩溃,页面上表现为白屏或刷新后会恢复。

另外,程序退出时要给CEF一点清理时间。在Application.Run结束后,GlobalCEFApp.Free之前,确保所有浏览器组件都已释放,否则会有内存报错或崩溃日志。防止这个问题的一个简单办法是:在窗体FormClose里先释放Chromium组件,或者设置GlobalCEFApp.CloseAllBrowsers。我遇到过客户系统关机时弹内存错误弹窗,最后就是退出顺序没处理好,调整后问题消失。

5.4 从旧版升级注意事项

如果你是从更老的CEF4Delphi版本(比如79、65之类的)升级到86.0.23.0,有几件事要确认。

第一,API变化。CEF4Delphi在版本迭代中偶尔会调整接口名称和事件签名,旧工程编译时会有一堆类型不匹配。建议对照官方Demo逐个修正,不要用全局替换这种粗暴方式。第二,缓存目录格式变了。Chromium的缓存数据库不向前兼容,升级后第一次启动如果页面显示异常或者登录状态丢失,直接把Cache目录删掉重新生成就行,这是正常现象。第三,体积和资源占用。86内核比老版本内核大不少,安装包体积会膨胀,同时进程数增多,这是内核升级的必然代价。如果你的目标机器硬件比较老,建议仔细测试一下内存占用上限。

给一个通用的建议:CEF4Delphi版本升级后,至少留一周的回归测试时间,重点覆盖本地页面加载、JS交互、文件下载、打印预览这些常用功能,因为这些模块在不同Chromium版本中的行为差异最明显。

我在实际项目中通常不会追最新版,而是一个大版本稳定后再考虑升级。86.0.23.0这个版本我自己在好几个老项目中用了挺长时间,最大的感受是它把“能用”和“好用”之间的平衡拿捏得不错:可交付体积合理、内存可控、现代Web特性覆盖够全,团队接手也容易。如果你的Delphi项目正准备集成嵌入式浏览器,或者正在被IE内核的TWebBrowser折磨,可以先用这个带支持库版本跑通一个Demo,亲手验证一遍JS交互和高分屏表现,再决定是否把它引入正式项目。如果后续遇到具体的API问题,按我上面给的排查思路一条条过,基本能覆盖大多数坑。

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

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

Java与Python日期处理实战:构建国际化纪念日提醒工具

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

作者头像 李华
网站建设 2026/9/3 1:29:28

QC-LDPC误码率仿真实战:Matlab避坑指南与工程化框架

简介:本资源是一份面向通信工程专业学生与研究人员的QC-LDPC码MATLAB误码率仿真完整实现,聚焦信道编码性能评估这一核心问题,适用于无线/光纤通信系统课程设计、毕业设计及算法验证场景。压缩包共5个文件(4个.m脚本1个.mat校验矩阵…

作者头像 李华
网站建设 2026/9/3 1:27:30

异业联盟小程序源码架构与实战部署指南

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

作者头像 李华
网站建设 2026/9/3 1:24:59

深度学习环境搭建指南:Anaconda与PyTorch安装配置详解

深度学习的入门门槛,很大程度上不在算法本身,而在一套能跑通训练流程的开发环境。很多初学者一开始信心满满,结果在装软件、配依赖、解决版本冲突这一步就耗了好几天。实际上,只要理解了装环境的核心逻辑,掌握了Anacon…

作者头像 李华
网站建设 2026/9/3 1:24:54

历档DA580档案盒打印机:厚介质自动进纸与批量打印实战拆解

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

作者头像 李华