新闻详情

新闻详情

首页 / 资讯中心 / 详情

Delphi中用TEdgeBrowser嵌入现代浏览器,实现页面与代码双向通信

发布时间:2026/10/1 20:46:05来源:尧图网络
Delphi中用TEdgeBrowser嵌入现代浏览器,实现页面与代码双向通信
1. 项目定位Delphi 里的现代浏览器组件到底能做什么先交代一个背景做 Delphi 开发的人十有八九会遇到“程序里要嵌一个网页”的需求。以前大家第一反应是拖一个TWebBrowser然后老项目里经常被 IE 内核的各种兼容性问题搞到怀疑人生。后来微软出了 Edge Browser 浏览器组件Delphi 从 10.4 开始也原生提供了TEdgeBrowser控件底层直接对接 WebView2 Runtime核心是 Chromium 内核等于说你能在 Delphi 程序里内嵌一个真正的“现代浏览器”而不是那个用了快二十年、连 flex 都支持不好的老古董。这个项目/实践的定位很清楚给 Delphi 桌面程序补上一块现代 Web 渲染能力。它能做的不是简单的“显示网页”而是让网页和 Delphi 代码互相通信、双向调用同时把页面渲染性能拉到 Chrome 级别。适合这几类人参考还在维护老项目被TWebBrowser逼疯想低成本换内核。新项目要做混合架构界面用 HTML/CSS/JS 写业务逻辑用 Delphi 管。内部工具需要展示 Web 地图、大屏图表、在线编辑文档这类重度前端页面。想给老系统做“现代化换皮”但不想把后端重写一遍。我这次会从环境准备、组件搭建、核心代码、常见坑一路讲到底基本覆盖平时项目里真正会用到的部分。如果你已经有 Delphi 基础可以直接跳到代码段如果是新手也别慌每一步我都会解释为什么这么做。1.1 老牌 TWebBrowser 的债TEdgeBrowser 来还很多老 Delphi 项目里嵌浏览器的标准做法是TWebBrowser它封装的是 IE 内核。在 XP 时代这可能没什么问题但现在打开一个现代管理后台大概率会遇到这些问题ES6、TypeScript 编译出来的代码直接白屏或者语法报错。CSS Grid、Flexbox 布局乱掉表单对不齐。WebSocket 连接不稳定实时消息推送做不了。页面稍微复杂点内存涨得飞快关闭页面也不释放。IE 内核还不只是兼容性问题它是跟着 Windows 系统里的 IE 版本走的。就算你代码里指定了渲染模式用户机器上 IE 版本不同效果也不一样。这个坑一旦在验收阶段爆出来调试成本非常高。换到TEdgeBrowser以后这些问题基本都消失了因为它用的是 Chromium 内核和平时调试用的 Chrome 浏览器保持同源前端工程师说“我本机没问题”到你这边大概率也不会有问题。1.2 Edge Browser 组件不是浏览器本身而是浏览器外壳这里有个容易误解的概念TEdgeBrowser不是一个独立的浏览器程序可以把它理解成“浏览器的外壳”或者“嵌入层”。真正干活的是 WebView2 Runtime它就是一个可以随时更新、独立分发、专门给桌面程序嵌入用的浏览器内核运行时。所以部署的时候有个大前提目标机器上必须装 WebView2 Runtime。好消息是 Windows 11 自带Windows 10 更新到最新版本后也大概率自带坏消息是有些精简版系统、离线内网机器上确实没有这时候需要手动安装 Runtime 安装包。安装完以后TEdgeBrowser才能正常创建工作区并加载页面。用生活化一点的类比TEdgeBrowser像个电视机壳子WebView2 Runtime 是里面的电视主板和屏幕。壳子做得再好没主板也开不了机。你没法只靠 Delphi 代码凭空造出 Chromium必须先有一个可用的 Runtime。1.3 我自己定义的“好用”标准我判断一个浏览器组件能不能进项目就看三条能不能加载现代前端应用而不是只打开静态 HTML。能不能从 Delphi 里执行页面里的 JavaScript。能不能让页面反过来调用 Delphi 的本地能力。这三点TEdgeBrowser都能做到而且方式很直接。后面我会详细拆解每一条怎么写。当然它也不是没有缺点比如安装依赖比老控件多、异步回调需要考虑线程问题、不同的 Delphi 版本细节上有些差异。但总体来看这笔账非常划算尤其是新项目从第一天就用能少走很多弯路。2. 环境准备装 Runtime、拖组件、写第一段代码2.1 WebView2 Runtime 不是 Edge 浏览器很多同事第一次搞TEdgeBrowser的时候都会问我电脑上有 Edge 浏览器是不是就不需要装 Runtime 了答案是不一定。Edge 浏览器和 WebView2 Runtime 是两套东西。虽然它们共用 Chromium 内核但 WebView2 Runtime 有自己独立的更新通道和文件目录。Windows 11 和更新过的 Windows 10 会默认带上但如果你是把程序部署到服务器、虚拟桌面或者一堆几年没更新的机器上就一定要先检查 Runtime 是否存在。检查方法不复杂不用写代码在命令行跑一下reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5} /v pv能查出来版本号说明 64 位系统里已经有 WebView2 Runtime 了。查不到就装一下。官方提供了 Evergreen 类型的在线安装包和离线安装包内网部署最好用离线安装包避免每台机器都去外网拉一次。提示有些机器上已经安装了 32 位浏览器内核组件但你的 Delphi 程序是 64 位编译的运行时也会出现加载失败。后面第 4 部分我会专门说这个坑。2.2 在 IDE 里找到 TEdgeBrowser 组件Delphi 10.4 及以上版本组件面板里可以直接找到TEdgeBrowser一般在 Internet 分类下。如果你用的版本比较老比如 Delphi XE2 或者更早原生没有这个控件可以试试开源的 WebView4Delphi 方案思路类似但交互接口略有差别。我个人建议条件允许就升到 10.4 以上用官方组件最省心。拖一个TEdgeBrowser到窗体上以后建议先设置两个属性DefaultURL组件初始化后默认打开的地址。UserDataFolder浏览器用户数据目录用来存放 Cookie、缓存、localStorage 等。UserDataFolder很多人会忽略但这个东西非常重要。默认情况下如果你不指定组件会使用系统临时目录下的一个路径。这会导致每次开机或者程序崩溃后数据丢失而且多个进程共用同一个目录还会互相锁。实际项目中我都会指定到程序目录下的一个固定文件夹或者C:\ProgramData\公司名\项目名\WebViewData。2.3 第一段能跑的代码新建一个窗体放一个TEdgeBrowser、一个TButton、一个TEdit然后在按钮点击事件里写procedure TFormMain.Button1Click(Sender: TObject); begin EdgeBrowser1.Navigate(Edit1.Text); end;按 F9 运行输入一个网址比如https://www.example.com页面就会显示在窗体里面。这是最简单的用法比旧TWebBrowser写起来差不多但渲染结果完全不是一个级别。如果你本地正好有 HTML 文件也可以直接用本地路径EdgeBrowser1.Navigate(file:///D:/Project/Demo/index.html);或者直接把 HTML 字符串塞进去适合做纯前端界面嵌入EdgeBrowser1.NavigateToString( htmlbody styledisplay:flex;align-items:center;justify-content:center; font-size:32px;background:#1e1e1e;color:#00ff88;Hello Edge/body/html);第一次跑通以后你会发现TEdgeBrowser的加载速度和渲染效果基本就是 Chrome 的内嵌版。这时候你已经把一个现代浏览器放进 Delphi 程序了。2.4 初始化状态机别在 WebView 创建完成前乱调用很多新手第一次用TEdgeBrowser时会在FormCreate里直接写Navigate结果发现有时候能打开有时候打不开甚至偶尔报错。原因在于TEdgeBrowser的初始化是异步的。控件拖到窗体上只是一个壳真正的内核进程需要一定时间才能创建好。稳妥的做法是在OnCreateWebViewCompleted事件里做后续导航procedure TFormMain.EdgeBrowser1CreateWebViewCompleted( Sender: TObject; AResult: HRESULT); begin if Succeeded(AResult) then EdgeBrowser1.Navigate(https://www.example.com) else ShowMessage(WebView 创建失败: 0x IntToHex(AResult, 8)); end;如果你在FormCreate里调用Navigate又能正常工作那多半是组件内部已经在Load阶段自动初始化完成了。但为了稳定特别是页面里还要执行 JS 的场景我建议统一等CreateWebViewCompleted再走后续逻辑。这个习惯能帮你避开一大半莫名其妙的白屏问题。3. 核心实现网页与 Delphi 双向通信3.1 在页面里执行 JavaScript做桌面应用嵌网页最常用的一个功能就是“点一下 Delphi 按钮让网页更新数据”。这个操作在TEdgeBrowser里叫执行脚本。常见写法procedure TFormMain.ButtonExecClick(Sender: TObject); const JS_CODE document.getElementById(lblResult).innerText Hello from Delphi;; begin EdgeBrowser1.ExecuteScript(JS_CODE); end;如果脚本执行完以后你想拿到返回值比如读取页面上的某个数据那就需要用到回调函数。我这里写一个示例读取页面标题procedure TFormMain.ButtonReadTitleClick(Sender: TObject); begin EdgeBrowser1.ExecuteScript( document.title;, procedure(AResult: HRESULT; const AResultJson: string) begin if Succeeded(AResult) then Memo1.Lines.Add(页面标题: AResultJson) else Memo1.Lines.Add(脚本执行失败: 0x IntToHex(AResult, 8)); end); end;注意回调里拿到的AResultJson不一定是你想象中的纯文本。如果页面返回的是一个字符串它可能会带有 JSON 的双引号比如我的页面。这是 WebView2 的机制它把执行结果转成 JSON 再传回给 Delphi。遇到这种情况不用慌去掉首尾引号再做展示即可。3.2 从网页反过来调用 DelphiWebMessage网页里执行 JavaScript 只是“单向通道”真正好用的是“双向通信”。TEdgeBrowser提供了一套基于window.chrome.webview.postMessage的消息机制让网页能主动把数据传给 Delphi 程序。网页端代码就一行window.chrome.webview.postMessage(from_page: hello);也可以传复杂一点的对象比如window.chrome.webview.postMessage(JSON.stringify({ type: saveData, id: 1001, content: 这是网页里的内容 }));Delphi 端在TEdgeBrowser的OnWebMessageReceived事件里接收procedure TFormMain.EdgeBrowser1WebMessageReceived(Sender: TObject; const AEventArgs: TWebMessageReceivedEventArgs); begin Memo1.Lines.Add(网页消息: AEventArgs.WebMessageAsJson); end;这样网页和 Delphi 程序之间就能互相喊话了。比如前端页面上有一个“保存”按钮点击后把表单数据postMessage给 DelphiDelphi 收到后写数据库、调打印机或者调用本地 DLL再通过ExecuteScript把结果回写到页面上。这个模式非常适合做混合架构界面、交互、图表全部交给 Web 前端桌面端只负责本地能力比如串口、数据库、文件系统、硬件设备。比起老式的界面开发前端生态里的轮子实在太多了做出来的界面也好看得多。3.3 弹窗、新窗口和下载行为的接管网页里最常见的一套操作是window.open打开新窗口、弹 alert、触发下载。默认情况下TEdgeBrowser对window.open会尝试打开一个系统窗口这往往不是我们想要的。实际项目中我一般会在OnNewWindowRequested事件里拦住它让它转成当前控件导航procedure TFormMain.EdgeBrowser1NewWindowRequested(Sender: TObject; const AEventArgs: TNewWindowRequestedEventArgs); begin AEventArgs.Handled : True; EdgeBrowser1.Navigate(AEventArgs.Uri); end;这样网页里的“在新窗口打开”就不会跳出去而是在当前区域内导航。如果你希望它真的弹出一个新窗体那就比较麻烦了需要再创建一个窗口、再放一个TEdgeBrowser然后把请求交给新控件的地址去加载。弹窗alert和confirm也会影响体验。默认情况下网页弹出 JS 对话框会以 WebView 弹窗的形式出现如果是批量循环弹窗用户会很崩溃。官方底层提供了SetWindowDisplayAffinity等高级接口能自定义弹窗行为但平时够用的话我建议尽量在页面里改成页面内提示组件不要依赖原生弹窗。3.4 把异步回调包成同步等待的土办法上一节讲的ExecuteScript回调本质上是异步的。异步是好事不阻塞 UI但有时候你就是想要一个“同步拿结果”的效果比如在一个业务函数里必须先拿到页面数据再继续往下走。这时候可以用TEvent和线程配合写一个简单的等待器。我提供思路不完整贴代码以免你直接抄错先把脚本传给ExecuteScript。在回调里给一个TEvent发信号并保存结果。在主流程里调用WaitFor超时时间设置成 3 秒或 5 秒。但这里要非常注意WaitFor如果在 UI 线程里执行页面里的回调可能也要回到 UI 线程两者互相等就会死锁。所以我实际项目里这么做的时候都会把整个业务放到工作线程里或者干脆不用同步等待直接用回调驱动流程。能用回调解决的就别硬搞同步这是 WebView2 开发里很重要的一个原则。4. 避坑清单我踩过的六个坑4.1 白屏一大片99% 是 Runtime 没装齐我给一个客户做升级的时候程序在我电脑上跑得好好的拿过去就白屏。排查了半天最后发现是那台机器只装了 Edge 浏览器没有独立的 WebView2 Runtime。这个问题特别有迷惑性因为机器上看起来“有 Edge”但你的程序用的 Runtime 没装。解决方案就是去微软官网下载 Evergreen Runtime 安装包部署的时候顺手装一下。如果你做的是安装包可以在安装流程里加一个环境检查检测不到 Runtime 就提示用户安装或者直接静默安装。注意官方安装包有个特点如果机器上已经装了 WebView2 Runtime再次覆盖安装时不会让你选择路径大体上就是“覆盖安装暂不支持更改路径”。想换目录必须先卸载再重装这个也算部署时的一个冷知识。4.2 32 位程序和 64 位程序的问题Delphi 可以编译出 32 位程序也可以编译出 64 位程序。WebView2 Runtime 也有对应的 32 位、64 位版本。正常情况下 Evergreen Runtime 会自动处理但在一些奇怪环境下32 位程序装的是 64 位 Runtime或者反过来就会出现“内核组件已经安装却加载失败”的诡异现象。我遇到过的实际情况是程序是 32 位编译的用户机器上偏偏只装了 64 位 RuntimeTEdgeBrowser创建 WebView 失败报错信息还不明确。解决办法就是检查机器上有没有对应的 32 位版本。部署时最好两个架构的 Runtime 都准备或者在安装脚本里根据主程序位数自动判断装哪个。4.3 用户数据目录被锁程序崩溃UserDataFolder这个目录不是随便给你放的。WebView2 会在这个目录里维护数据库、缓存文件、Cookie 等。如果同一个目录被两个进程同时占用后启动的那个进程可能直接创建失败。项目里遇到过一个典型场景程序主窗口关掉了但后台线程没完全退出用户马上又打开新实例两个实例抢同一个UserDataFolder结果新窗口一片白。后来我在启动逻辑里加了单实例检测并且把UserDataFolder按用户或者按会话区分开比如EdgeBrowser1.UserDataFolder : D:\AppData\EdgeProfile\ TPath.GetFileNameWithoutExtension(ParamStr(0));这样不同版本的程序不会互相干扰同一时刻只允许一个实例访问自己的目录问题就消失了。4.4 缓存和 localStorage 不生效如果你发现网页里存了 localStorage重启程序以后没了或者页面明明更新了显示的却是旧版本大概率是UserDataFolder没有固定下来。在不设置该属性的情况下组件可能使用临时目录系统清理垃圾文件后数据就没了。固定的做法和上面一样把UserDataFolder指向一个稳定的、有写权限的路径。权限也要注意如果你把程序安装在C:\Program Files下默认可能没有写权限最好放到C:\ProgramData或当前用户目录下。4.5 ExecuteScript 返回值拿不到或解析出错执行 JS 的脚本里有语法错误时回调里的 HRESULT 会失败但错误信息不一定详细。排查思路是先把这个脚本复制到 Chrome 开发者工具的 Console 里跑一遍确认语法没问题再来查 WebView2 的调用。很多时候不是你代码问题而是脚本本身写错了。另外返回值如果是 DOM 元素对象直接返回给 Delphi 是拿不到的WebView2 只能返回 JSON 序列化后的数据。你需要先在页面里把内容处理成基础类型比如用document.getElementById(...).innerText取出文本再返回。4.6 从 TWebBrowser 平迁到 TEdgeBrowser不能只换控件老项目替换控件时最容易犯的错误是把界面上的TWebBrowser直接删掉换TEdgeBrowser然后代码里browser.Navigate照抄。表面上都能跑但事件模型完全不同比如TWebBrowser的Document对象和TEdgeBrowser的ExecuteScript完全是两套机制。老代码里通过OleObject操作 DOM 的写法全部作废。弹窗、下载、脚本错误的处理逻辑都要重写。所以我的建议是平迁时不要追求一步到位先把核心导航、消息通信跑通再把老页面里的 ActiveX 依赖、IE 专属代码全部去掉最后再替换事件逻辑。这个周期比想象中长但换完以后你会觉得特别值。5. 实战体验与扩展思路5.1 性能实测我用同样一个后台管理页面做过对比老TWebBrowser打开要两三秒而且滚动有明显掉帧TEdgeBrowser打开基本是一瞬间滚动、缩放、表格筛选都很流畅。内存占用确实比老控件高因为 Chromium 本身就是一个完整浏览器内核一个页面占用两三百兆很正常。如果程序要长期挂着可以考虑把不需要的页面及时置空或者关闭。但换来的是用户观感上的巨大提升这点代价值得。多页面标签的话每个TEdgeBrowser都会有自己的内核进程开销不建议一个窗体上放太多实例。真要做类似浏览器的多标签建议复用同一个TEdgeBrowser切换标签时只是导航到不同 URL而不是每个标签都放一个控件。5.2 混合架构落地时的一些建议如果你决定用网页做界面、Delphi 做业务我建议在项目初期就把通信协议定下来。比如网页给 Delphi 发消息统一用 JSON 格式{ action: saveDocument, data: { id: 1, title: 测试 } }Delphi 端收到后先解析action再根据具体业务分发。不要写一堆零散的postMessage字符串后期维护会非常痛苦。本地文件加载如果遇到跨域问题建议起一个本地 HTTP 服务或者让页面里所有请求都走同一个file://目录。有些前端工程用 Vite/Webpack 开发时地址是http://localhost:5173发布后用file://打开会踩到资源路径和跨域问题。我的做法是开发时连接本地开发服务器发布时把构建产物打包到程序目录用Navigate(file:///...)加载并且在前端工程里配置相对路径。5.3 还能延展出的玩法TEdgeBrowser真正用起来以后你会发现自己手里多了一个很强大的混合开发工具。比如内嵌地图组件做 GIS 功能前端直接用 Leaflet/OpenLayersDelphi 负责调数据库。大屏可视化面板前端用 ECharts 或 AntVDelphi 只负责定时推送数据。在线文档编辑前端用富文本框架Delphi 负责保存到本地文件。如果项目里已经接入了 Delphi 的 OCR 库比如 tesseract 封装识别结果可以直接通过ExecuteScript回填到网页上让前端负责展示和交互整体体验会非常流畅。有一点要记住WebView2 的更新是持续的新特性会越来越多。所以做架构设计时尽量别把业务逻辑写死在页面里保持网页端和 Delphi 端的松耦合后续升级才不会拆东墙补西墙。5.4 最后说一个我自己的习惯每次新建一个用到TEdgeBrowser的项目我都会先写一个小的测试页里面放一个按钮点一下向 Delphi 发消息再放一个输入框等 Delphi 来填内容。先把这套双向通信打通再做后面复杂功能。这样能提前把 Runtime 问题、初始化问题、消息通道问题全部暴露出来而不是等到项目后期才发现底层走不通。你如果正准备在 Delphi 项目里接 Edge Browser 组件建议也按这个顺序来先跑通导航再打通通信最后再处理业务细节。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

无人机罂粟检测数据集 | 罂粟检测 无人机巡查 禁毒监测 非法种植 目标检测9137期 2026/10/1 21:42:07

无人机罂粟检测数据集 | 罂粟检测 无人机巡查 禁毒监测 非法种植 目标检测9137期

无人机罂粟检测数据集 | 罂粟检测 无人机巡查 禁毒监测 非法种植 目标检测9137期 数据集概述 本数据集专注于无人机航拍视角下的非法罂粟种植识别,服务于公安禁毒、林业监管及生态保护。数据涵盖罂粟植株的航拍影像,适配大面积野外巡查、隐蔽种植排查及…

阅读更多 →
静默活体检测项目实战  算法原理【附代码】 2026/10/1 21:42:07

静默活体检测项目实战 算法原理【附代码】

1. 什么是静默活体检测?静默活体检测,就是在用户不做任何动作、无感知的情况下,判断镜头前那张脸是真人还是假体。 不需要眨眼、张嘴、摇头,一帧图像就能给出真假结论。2. 为什么需要它?因为假脸太容易做了。 打印照片…

阅读更多 →
SpringBoot+Vue前后端分离校园网站管理系统实战解析 2026/10/1 21:42:07

SpringBoot+Vue前后端分离校园网站管理系统实战解析

接这个项目的时候,对方说得很轻巧:"给信息学科部做个校园网站,展示一下日常动态就行。"结果等我梳理完需求才发现,这不是一个展示型官网,而是一个要支撑部里日常运转的轻量级信息系统。公告发布、课程资料下…

阅读更多 →
文具行业GEO推广经验丰富,云熵科技针对开学季场景优化投放,覆盖合肥多所学校 2026/10/1 21:42:00

文具行业GEO推广经验丰富,云熵科技针对开学季场景优化投放,覆盖合肥多所学校

九月第一缕晨光穿过梧桐叶,落在合肥街头。书包带在肩头勒出细小的褶皱,文具店里铅笔与削笔刀的碰撞声清脆作响,家长牵着孩子的手,在货架前反复比对每一支笔的顺滑度、每一本本子的纸张厚度。开学季,是无数家庭郑重其事…

阅读更多 →
std::optional 完全指南:别再用 -1 和 nullptr 表达「没有值」 2026/10/1 21:41:54

std::optional 完全指南:别再用 -1 和 nullptr 表达「没有值」

用 -1 表示「没找到」、用 nullptr 表示「没配置」&#xff0c;这类约定在 C 里活了几十年&#xff0c;代价是每个调用方都得记住「-1 是特殊值」并且每次都记得判断。C17 的 std::optional<T> 把这件事变成类型系统的一部分&#xff1a;函数的返回值类型直接写着「可能没…

阅读更多 →
借助AI工具学习:控制科学研究方向——目标识别与环境理解 2026/10/1 21:41:54

借助AI工具学习:控制科学研究方向——目标识别与环境理解

目标识别与环境理解&#xff5c;两周试错学习清单 目标&#xff1a;不碰昂贵硬件&#xff0c;用电脑开源代码&#xff0c;快速体验控制学科视角的环境感知&#xff0c;判断自己是否适合这个方向。 前提环境&#xff1a;Python3.10&#xff0c;PyTorch&#xff0c;CUDA&#xff…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉