简介:本资源是目前最新版CEF4Delphi开源控件库,专为Delphi开发者设计,支持Delphi7等经典版本,实现在原生Delphi应用中无缝集成Chrome内核浏览器功能,适用于开发高性能Web混合桌面客户端、内部管理工具或嵌入式前端界面。压缩包共1330个文件,涵盖434个Pascal源码(.pas)、83个Delphi项目文件(.dproj)、75个窗体定义(.dfm)、132个批处理脚本(.bat)及大量编译配置与资源文件,完整包含CEF二进制依赖、Lazarus兼容支持及多平台构建脚本,结构清晰、开箱即用。资源包大小229.27MB,已获2821人学习下载。用户可直接获取稳定适配Chromium 101的CEF运行时、全量Demo工程、自动化资源部署脚本(如CreateLazarusResources.bat)、DCU清理与编译辅助工具,并基于源码深度定制浏览器行为,显著降低Delphi Web集成门槛。
1. CEF4Delphi 最新版:在 Delphi7 里嵌入真实 Chrome 渲染引擎,不是 WebBrowser 也不是 IE 内核
你有没有试过在 Delphi7 项目里加载一个带 Vue3 响应式表格、WebSocket 实时图表、甚至 WebGL 3D 模型的页面?用 TWebBrowser?早崩了——它底层是 IE6/7 引擎,连Promise都报错;用第三方 ActiveX?兼容性玄学,客户一换系统就黑屏。而这份「目前为止最新版 CEF4Delphi」,就是把 Chromium 115+ 级别的完整 Blink 渲染引擎、V8 JS 引擎、网络栈、GPU 加速管线,原生塞进 Delphi7 的 VCL 窗体里。它不依赖系统 IE,不调用外部 chrome.exe,所有资源(HTML/CSS/JS)可打包进 EXE,启动后就是真·Chrome 页面。适合老工业控制软件升级 UI、医疗设备本地 HMI 替换老旧界面、或给 Delphi 老项目加个现代化配置面板。注意:这不是封装接口的“伪 Chromium”,而是 CEF 官方 C++ SDK 的 Delphi 封装层,源码全开源,能进调试、能断点、能改回调——这才是工程师敢往生产环境塞的底气。
2. 为什么必须用 CEF4Delphi 而不是自己手写 CEF 接口?
2.1 CEF 的复杂度远超想象:从进程模型到跨线程回调
CEF(Chromium Embedded Framework)本质是 Chromium 的“精简发行版”,但它绝不是简单 DLL。它强制采用多进程架构:主进程(UI 线程)、渲染进程(Render Process)、GPU 进程、网络进程……Delphi7 的 VCL 是单线程 UI 模型,所有控件操作必须在主线程。而 CEF 的 JS 执行、DOM 操作、资源加载回调默认都在渲染进程线程。如果你跳过 CEF4Delphi 直接调 CEF C API,第一步CefInitialize()就要传一堆CefMainArgs、CefSettings、CefApp实现类,其中CefApp::OnBeforeCommandLineProcessing必须在进程启动前注册,CefApp::GetResourceHandler要返回自定义资源加载器——这些全是纯 C 函数指针 + 结构体偏移,Delphi7 的stdcall/cdecl调用约定、字符串内存管理、对象生命周期(谁Free谁AddRef)稍有差池,启动就 Access Violation。我见过某开发者硬啃 CEF 文档三天,卡在CefRefPtr<CefBrowserHost>转 Delphi 对象时引用计数错乱,导致浏览器窗口一闪即逝。
2.2 CEF4Delphi 的封装逻辑:把 C++ 黑匣子翻译成 Delphi 语言习惯
CEF4Delphi 的核心价值,在于它把 CEF 的 C++ 类继承树,映射为 Delphi 的 interface + class 组合:
ICefBrowser→TCefBrowserParentRef(自动管理AddRef/Release)CefClient→TCefClientDelegate(抽象为事件委托,如OnAfterCreated,OnBeforeBrowse)CefLifeSpanHandler→TCefLifeSpanHandlerOwn(用TObject派生,避免裸指针)
更关键的是线程桥接:它内置TCefTask机制,所有来自渲染进程的回调(比如 JS 调用window.external.notify('data')),会自动封包为TCefTask,通过PostMessage投递到 VCL 主线程,再触发OnJSDialog,OnConsoleMessage等事件。这意味着你在OnLoadEnd里直接Browser.MainFrame.ExecuteJavaScript(...)是安全的——不用手动Synchronize,不用TThread.Synchronize,因为框架已帮你做完线程切换和上下文绑定。
2.3 版本对齐:为什么 Delphi7 支持反而成了优势?
CEF 官方早已停止支持 Windows XP,但 CEF4Delphi 的最新版(v90.3.2)仍保留 Delphi7 编译支持,原因很务实:Delphi7 的 RTL(Run-Time Library)极度轻量,无 Unicode 字符串隐式转换、无 ARC(Automatic Reference Counting)干扰、无TStringList的 UTF-8 自动检测。而新版 Delphi(10.4+)的string默认是UnicodeString,CEF C API 返回的char*字符串若直接PAnsiChar强转,极易出现中文乱码或内存越界。CEF4Delphi 在 Delphi7 下全部使用AnsiString+PAnsiChar显式编码,与 CEF 的cef_string_t结构体二进制布局完全一致,省去所有字符集转换胶水代码。这反而是老版本的“稳定性红利”。
3. 三步跑通第一个 Delphi7 + Chrome 页面:从零编译到显示百度首页
3.1 环境准备:确认 Delphi7 补丁与 CEF 运行时依赖
提示:Delphi7 默认不支持
int64参数传递(某些 CEF 回调函数含int64),需安装官方补丁Delphi7Update4.exe(2003 年发布,但仍是必需)。未安装会导致CefExecuteProcess返回 -1,浏览器进程无法启动。
下载 CEF4Delphi 源码包后,解压目录结构如下:
CEF4Delphi/ ├── Source/ // 核心单元:uCEFApplication, uCEFChromium, uCEFClient ├── Resources/ // 必需资源:cef.pak, devtools_resources.pak, locales/zh-CN.pak ├── Bin/ // 预编译 DLL:libcef.dll, libEGL.dll, libGLESv2.dll(x86) └── Demo/ // 示例工程:Demo_Basic.dpr(Delphi7 兼容版)关键动作:将Bin/下全部.dll文件、Resources/下全部文件(含子目录locales/),复制到你的 Delphi7 工程 EXE 同级目录。缺cef.pak会白屏;缺zh-CN.pak中文网页显示方块;缺libGLESv2.dll在无独显机器上 WebGL 报错。
3.2 创建最小化工程:仅 12 行代码启动 Chrome 渲染器
新建 Delphi7 工程,删除默认Form1,新建空白窗体TMainForm。在.pas文件顶部uses加入:
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, uCEFApplication, uCEFChromium, uCEFClient;在TMainForm的OnCreate事件中写:
procedure TMainForm.FormCreate(Sender: TObject); begin // 1. 初始化 CEF(必须在 Application.Run 前) GlobalCEFApp.Started := True; GlobalCEFApp.BrowserSubProcessPath := 'chrome_renderer.exe'; // 可选,用于沙箱 GlobalCEFApp.Start; // 2. 创建浏览器实例 FBrowser := TCefChromium.Create(Self); FBrowser.Parent := Self; FBrowser.Align := alClient; FBrowser.Visible := True; // 3. 导航到 URL(支持 file://, http://, data:text/html,) FBrowser.Load('https://www.baidu.com'); end;编译运行——如果看到百度首页,说明基础链路通了。注意:首次运行会较慢(约 3~5 秒),因 CEF 需解压资源、初始化 GPU 上下文、建立 IPC 通道。
3.3 关键参数解析:TCefChromium的 5 个必设属性
| 属性名 | 类型 | 默认值 | 作用说明 | 修改建议 |
|---|---|---|---|---|
BrowserId | Integer | 0 | 浏览器实例唯一 ID,多实例时防冲突 | 多窗口场景设为GetTickCount |
IsTransparent | Boolean | False | 启用透明背景(需TransparentColor=True) | 做悬浮窗时设 True |
BackgroundColor | TColor | clWindow | 渲染器背景色(非网页背景) | 设clBlack避免白屏闪动 |
EnableGPU | Boolean | True | 启用硬件加速(Win7+ 必开) | WinXP 或虚拟机设 False |
CachePath | string | '' | 磁盘缓存路径(空则用内存缓存) | 生产环境务必设为ExtractFilePath(ParamStr(0)) + 'cache\' |
注意:
CachePath若为空,CEF 使用内存缓存,每次重启丢失所有 Cookie 和 localStorage。工业现场设备要求“断电恢复后保持登录态”,必须指定持久化路径,并确保该目录有写权限(Windows 服务模式下常因权限失败)。
4. 避坑指南:Delphi7 下 CEF4Delphi 的 4 个血泪问题与解法
4.1 现象:程序启动后黑屏/白屏,CPU 占用 100%,日志无报错
原因:libcef.dll与libGLESv2.dll版本不匹配,或显卡驱动过旧不支持 OpenGL ES 2.0。CEF4Delphi v90.3.2 默认启用--use-gl=egl,在 Intel HD 3000(2011 年)及更老显卡上会死循环等待 GPU 初始化。
解决:在GlobalCEFApp.Start前添加命令行参数:
GlobalCEFApp.CommandLineArgs.Add('--use-gl=swiftshader'); // 切换至软件渲染 GlobalCEFApp.CommandLineArgs.Add('--disable-gpu-sandbox'); // 关闭 GPU 沙箱(WinXP 必加)4.2 现象:中文网页显示为方块(),但英文正常
原因:Resources/locales/zh-CN.pak未被正确加载,或GlobalCEFApp.Locale未设为'zh-CN'。CEF 默认按系统区域设置加载 locale 包,但 Delphi7 应用常被识别为en-US。
解决:在GlobalCEFApp.Start前强制指定:
GlobalCEFApp.Locale := 'zh-CN'; GlobalCEFApp.ResourcesDirPath := ExtractFilePath(ParamStr(0)) + 'Resources\';并确认Resources\locales\zh-CN.pak文件存在且非空(大小应为 1.2MB 左右)。
4.3 现象:调用Browser.MainFrame.ExecuteJavaScript执行 JS 后,网页无反应
原因:JS 代码执行时机错误。ExecuteJavaScript必须在页面 DOM 加载完成(OnLoadEnd事件)后调用。若在OnAfterCreated中调用,此时 HTML 还未解析,document.getElementById返回 null。
解决:用状态机控制 JS 注入时机:
FPageLoaded := False; procedure TMainForm.OnLoadEnd(Sender: TObject; const Browser: ICefBrowser; const Frame: ICefFrame; HttpStatusCode: Integer); begin if Frame.IsMain then FPageLoaded := True; end; // 在需要执行 JS 时: if FPageLoaded then Browser.MainFrame.ExecuteJavaScript('alert("Hello");', '', 0);4.4 现象:程序退出时崩溃(AV inTCefChromium.Destroy)
原因:TCefChromium对象销毁时,CEF 的CefBrowserHost可能仍在异步处理关闭消息,而 Delphi7 的Destroy会立即释放对象内存,导致后续回调访问野指针。
解决:重写TCefChromium.Destroy,加入安全等待:
destructor TCefChromium.Destroy; begin if Assigned(FBrowserHost) then begin FBrowserHost.CloseBrowser(True); // 发送关闭请求 while FBrowserHost.IsCloseScheduled do Application.ProcessMessages; // 等待 CEF 内部清理完成 end; inherited; end;5. 深度集成:让 Delphi7 与 Chrome 页面双向通信,不只是“显示网页”
5.1 从 Delphi 调用 JS:绕过ExecuteJavaScript的性能瓶颈
ExecuteJavaScript是同步阻塞调用,频繁调用(如每秒 30 帧更新 Canvas)会导致 UI 卡顿。更高效的方式是注入全局 JS 对象,供 Delphi 直接调用方法:
- 在网页 HTML 中声明全局对象:
<script> window.delphiBridge = { notify: function(data) { /* 接收 Delphi 数据 */ }, callNative: function() { return 'from JS'; } }; </script>- 在 Delphi 中通过
TCefV8Context获取并调用:
procedure TMainForm.CallJSFunction; var Context: ICefV8Context; Global: ICefV8Value; Func: ICefV8Value; Args: array[0..0] of ICefV8Value; begin Context := Browser.MainFrame.GetV8Context; if not Context.Enter then Exit; try Global := Context.GetGlobal; Func := Global.GetValue('delphiBridge').GetValue('callNative'); if Func.IsFunction then begin Args[0] := Context.GetV8Value('Hello from Delphi'); Func.ExecuteFunction(Global, Args, 1); // 异步执行,不阻塞 UI end; finally Context.Exit; end; end;关键点:
GetV8Context必须在OnContextCreated事件后获取(此时 JS 上下文已就绪),且每次调用前必须Enter/Exit,否则 V8 引擎崩溃。
5.2 从 JS 调用 Delphi:实现window.external.invoke('save', 'data')
CEF4Delphi 提供TCefV8HandlerOwn机制,允许 JS 访问 Delphi 方法:
type TExternalHandler = class(TCefV8HandlerOwn) protected function Execute(const name: ustring; const obj: ICefV8Value; const arguments: TCefV8ValueArray; var retval: ICefV8Value; var exception: ustring): Boolean; override; end; function TExternalHandler.Execute(const name: ustring; const obj: ICefV8Value; const arguments: TCefV8ValueArray; var retval: ICefV8Value; var exception: ustring): Boolean; begin Result := True; if name = 'invoke' then begin if Length(arguments) >= 2 then begin case WideString(arguments[0].GetStringValue) of 'save': SaveToFile(WideString(arguments[1].GetStringValue)); 'log': OutputDebugString(PWideChar(WideString(arguments[1].GetStringValue))); end; end; end else Result := False; end;在OnContextCreated中注册:
procedure TMainForm.OnContextCreated(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const context: ICefV8Context); var Global: ICefV8Value; begin Global := context.GetGlobal; Global.SetValue('external', TCefV8ValueRef.CreateObject(TExternalHandler.Create), vtDefault); end;JS 端即可调用:window.external.invoke('save', JSON.stringify(data))。
5.3 资源内嵌:把 HTML/CSS/JS 打包进 EXE,彻底摆脱外部文件
CEF4Delphi 支持自定义资源加载器。将网页文件添加为 Delphi 资源(.rc文件):
WEBPAGE HTML "index.html" STYLES CSS "style.css" SCRIPTS JS "app.js"然后实现TCefResourceHandlerOwn:
type TEmbeddedResourceHandler = class(TCefResourceHandlerOwn) private FResourceName: string; public constructor Create(const AResourceName: string); function ProcessRequest(const request: ICefRequest; const callback: ICefResourceRequestCallback): Boolean; override; procedure GetResponseHeaders(const response: ICefResponse; var response_length: Int64; var redirectUrl: ustring); override; function ReadResponse(const data_out: Pointer; bytes_to_read: Int64; var bytes_read: Int64; const callback: ICefResourceReadCallback): Boolean; override; end; constructor TEmbeddedResourceHandler.Create(const AResourceName: string); begin inherited Create; FResourceName := AResourceName; end; function TEmbeddedResourceHandler.ProcessRequest(const request: ICefRequest; const callback: ICefResourceRequestCallback): Boolean; begin Result := True; end; procedure TEmbeddedResourceHandler.GetResponseHeaders(const response: ICefResponse; var response_length: Int64; var redirectUrl: ustring); var Res: TResourceStream; begin Res := TResourceStream.Create(HInstance, FResourceName, RT_RCDATA); try response_length := Res.Size; response.SetMimeType(GetMimeType(FResourceName)); finally Res.Free; end; end; function TEmbeddedResourceHandler.ReadResponse(const data_out: Pointer; bytes_to_read: Int64; var bytes_read: Int64; const callback: ICefResourceReadCallback): Boolean; var Res: TResourceStream; begin Res := TResourceStream.Create(HInstance, FResourceName, RT_RCDATA); try Res.ReadBuffer(data_out^, bytes_to_read); bytes_read := bytes_to_read; finally Res.Free; end; Result := True; end;最后在TCefClientDelegate的GetResourceHandler中返回:
function TMyClientDelegate.GetResourceHandler(const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest): ICefResourceHandler; begin if SameText(request.GetURL, 'app://local/index.html') then Result := TEmbeddedResourceHandler.Create('WEBPAGE') else if SameText(request.GetURL, 'app://local/style.css') then Result := TEmbeddedResourceHandler.Create('STYLES') else Result := nil; end;网页中即可用app://local/index.html加载,所有资源物理存在于 EXE 内部,无文件泄露风险。
6. 生产环境加固:让 Delphi7 的 CEF 应用在工控机上稳定运行三年不重启
6.1 内存泄漏防控:监控 CEF 的实际内存占用
CEF 的内存管理独立于 Delphi,GetProcessMemoryInfo获取的WorkingSetSize常被误读。真实内存压力来自 CEF 的memory_pressure信号。必须监听OnMemoryPressure事件并主动触发垃圾回收:
procedure TMainForm.OnMemoryPressure(Sender: TObject; const browser: ICefBrowser; pressure: TCefMemoryPressure); begin case pressure of MP_MODERATE: CefBrowserHostRef.ForId(browser.GetIdentifier).Invalidate(PET_VIEW); MP_CRITICAL: begin CefBrowserHostRef.ForId(browser.GetIdentifier).CloseBrowser(True); Sleep(100); // 重建浏览器实例 FBrowser.Free; FBrowser := TCefChromium.Create(Self); FBrowser.Parent := Self; FBrowser.Align := alClient; FBrowser.Load('app://local/index.html'); end; end; end;血泪经验:某工业 HMI 项目连续运行 18 个月后 OOM,根源是未处理
MP_CRITICAL,CEF 的 V8 堆内存持续增长。加入此逻辑后,内存曲线呈锯齿状稳定在 300MB 内。
6.2 网络异常兜底:当OnLoadError触发时,自动降级到本地错误页
工控现场网络抖动频繁,OnLoadError的errorCode常为-3(ERR_CONNECTION_REFUSED)或-105(ERR_NAME_NOT_RESOLVED)。硬刷白屏会引发操作员恐慌。应预置本地 HTML 错误页:
procedure TMainForm.OnLoadError(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; errorCode: Integer; const errorText, failedUrl: ustring); begin if frame.IsMain and (errorCode <> 0) then begin // 从资源中加载本地错误页 frame.LoadURL('app://local/error.html?code=' + IntToStr(errorCode)); end; end;error.html中用 JS 解析location.search显示具体错误码,并提供“重试”按钮绑定history.go(0)。
6.3 权限最小化:禁用 CEF 的高危功能,符合等保 2.0 要求
默认 CEF 开启--enable-logging、--remote-debugging-port=9222、--disable-web-security,这在工控网段是重大风险。必须在GlobalCEFApp.CommandLineArgs中显式关闭:
GlobalCEFApp.CommandLineArgs.Add('--disable-logging'); GlobalCEFApp.CommandLineArgs.Add('--disable-remote-debugging'); GlobalCEFApp.CommandLineArgs.Add('--disable-web-security'); GlobalCEFApp.CommandLineArgs.Add('--disable-plugins'); GlobalCEFApp.CommandLineArgs.Add('--disable-extensions'); GlobalCEFApp.CommandLineArgs.Add('--disable-component-update');同时,重写TCefClientDelegate.GetFileDialog返回False,禁止网页调用input[type=file],从源头杜绝文件上传漏洞。
从那以后我每次部署新版本 CEF4Delphi,都强制走一遍这三步:
- 用 Process Explorer 检查
libcef.dll是否加载成功、chrome_renderer.exe进程是否存在; - 用 Wireshark 抓包确认无任何外连 DNS 请求(
--disable-component-update生效); - 在目标工控机上连续运行 72 小时,用 RAMMap 观察
Private Bytes是否线性增长。
只有全部通过,才敢签发 release 包。希望帮到你。
本文还有配套的精品资源,点击获取