ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Delphi 12.3下EmbeddedWB控件实战:IE内核网页嵌入与DOM交互

Delphi 12.3下EmbeddedWB控件实战:IE内核网页嵌入与DOM交互 简介本资源是专为Delphi 12.3.7即RAD Studio 12.3开发环境适配的EmbeddedWB嵌入式Web浏览器控件完整工程包面向Windows桌面应用开发者解决在Delphi原生界面中集成IE内核网页浏览、DOM操作与JavaScript交互等核心需求。压缩包共502个文件涵盖125个Pascal源码.pas、53个窗体设计文件.dfm、49个项目配置.dproj及配套资源.res/.dpr/.tlb等总大小仅1.68MB结构完整、开箱即用。已有96人下载学习适用于需快速嵌入网页功能的ERP、OA、本地化信息展示类项目开发。资源包含多个可直接编译运行的演示工程如VariousDemos、EmbeddedWBDemos附带资源复制脚本与典型窗体模板frmMain、umain等并提供OCX/DLL/TLB等底层组件支持便于理解控件注册、事件绑定与跨语言调用机制。 最近在给客户做一套老系统的界面升级项目历史包袱很重原来的窗体里塞了不少 WebBrowser 控件用来加载内网报表和本地业务页面。新版 Delphi 一到手我就开始找替代方案试了一圈下来最后装的是 EmbeddedWB for Delphi 12.3 这个控件包。说实话刚开始我对这种老牌 IE 内核封装是有点嫌弃的但实际用了两天发现它在 Delphi 12.3 上跑得比预期稳很多原本要写一堆 COM 逻辑的活都被它简化掉了。这篇文章就把这次折腾的过程、关键 API 的用法、以及安装和运行时踩过的一些坑都记录下来。如果你手头也是 Delphi VCL 项目想在窗体里嵌网页、做页面 DOM 操作、跟网页里的 JavaScript 双向通信这个控件包值得花十几分钟看一看。1. 为什么要用 EmbeddedWB它到底解决了什么问题1.1 原生 TWebBrowser 的痛点很多 Delphi 开发者都试过自带 TWebBrowser我的第一版方案也是从它入手的。TWebBrowser 其实就是微软IWebBrowser2COM 接口的一个薄封装能打开网页但稍微做深一点就很吃力页面一旦有脚本错误默认直接弹窗生产环境给客户看到特别掉价。要实现静默得自己去挂OnScriptError事件而且不一定能拦截干净。想要读取页面里的输入框、表格、某个 DIV 的内容必须自己QueryInterface拿IHTMLDocument2、IHTMLDocument3、IHTMLElement这一串接口代码重复率极高。页面里点击target_blank链接或者调用window.open默认会新开一个 IE 窗口根本没法控制在程序内部打开。要拦截新窗口得处理OnNewWindow2有时候还需要配合OnBeforeNavigate2事件之间怎么配合文档里写得很含糊。网页里的 JS 想调用 Delphi 这边的方法原生控件没有现成通道需要自己实现IDocHostUIHandler的GetExternal方法再写一个 COM 接口暴露给页面代码量大。如果只是展示一个静态页面TWebBrowser 够用。但只要涉及表单自动填充、页面数据采集、内嵌页面和主程序交互原生控件基本就是半成品。1.2 EmbeddedWB 的增强思路EmbeddedWB 做的事情简单说就是把上面这些反复要用到的能力全部封装成了可视化属性、事件和可直接调用的方法。拿最常用的几个场景举例设计期直接设置主页 URL运行启动就加载不用写一行 Naviage 代码。提供Silent、ShowErrorDialogs等属性脚本错误、JS 弹窗可以直接压掉。事件覆盖比 TWebBrowser 全很多OnBeforeNavigate2、OnDocumentComplete、OnNewWindow2、OnScriptError、OnNavigateError这些都直接暴露在设计期双击就能挂事件。内置获取文档对象的方法不用每次手动 QueryInterface。它还支持把控件注册为系统默认浏览器RegisterAsBrowser这个一般用不到但做深度定制时很灵活。一个很直观的感受是原生的 TWebBrowser 更像是“一个能显示网页的容器”而 EmbeddedWB 给人的感觉是“一个可以把网页当成对象去操作的控件”。它的核心思路没有绕开 IE 内核而是在 COM 接口之上多做了一层可靠的封装让 Delphi 开发者不需要面对 MSHTML 的原始接口细节。1.3 为什么没有选 CEF 和 WebView2这里也说明一下我不是没考虑过更现代的方案。CEFChromium Embedded Framework我用了不少次功能确实强HTML5 支持完整渲染效果也现代。但代价是分发体积大DLL 加资源文件动辄几十 MB而且版本升级频繁。WebView2 需要目标机器安装运行时很多政企内网机器没有这个环境或者 IT 部门不给装。回过头来看这个项目我需要的只是加载内网老业务页面、自动填表单、读取结果这些功能 IE 内核完全能覆盖。EmbeddedWB 体积小、无额外运行时、与 VCL 工程集成度高、升级成本低在这个场景下是更合适的选型。当然也要把话说清楚EmbeddedWB 的底层是 IE 内核HTML5 和 CSS3 支持程度跟 Chromium 系没法比。如果你的页面用了大量现代 Web 特性或者目标用户是公网环境那还是优先考虑 CEF 或 WebView2。选型没有绝对的好只有场景匹配度。2. 安装配置的坑与步骤2.1 解压与目录规划拿到EmbeddedWB for Delphi12.3.7z后我的习惯是先建一个统一的三方控件目录不要直接解压到 Delphi 的安装目录里。比如放在D:\Components\EmbeddedWB_D12这样后续切换 IDE 版本、备份环境都方便。解压之后目录结构大概是这样的Source或者src目录里面是核心单元源码。Packages或者Dpk目录里面是按照不同 Delphi 版本分好的包文件文件名里一般能看出 Delphi 12.3 对应的版本。Demos或Examples目录放了一些示例工程建议后面逐个打开看看。先花两分钟看一下有没有预编译好的.bpl文件和.dcu文件。如果有安装会快很多如果没有就需要走源码编译的路子。2.2 添加库路径打开 Delphi 12.3按顺序操作Tools Options Language Delphi Library在Library path里把Source目录添加进去。这里有个容易忽略的点如果控件包里区分了运行时源码和设计时代码只加一个根目录可能不够最好把子目录也加全。另外一个细节是平台选择。控件包安装时默认选中当前平台如果后续要编译 64 位程序记得在右上角把平台切换到Windows 64-bit再把路径重新检查一遍。很多控件在 32 位下编译正常切到 64 位就报找不到单元基本都是 Library path 在对应平台上没配置导致的。2.3 编译安装方法路径配好之后进入控件包目录找到对应 Delphi 12.3 的设计时包文件一般是EmbeddedWB_D12.dpk或类似命名。处理方式有两种第一种双击打开.dpk文件Delphi 会加载工程然后右键点击Install可以把它作为一个设计时包安装到 IDE。第二种通过Component Install Packages Add直接选择.bpl文件注册。我实际安装时发现有些版本的包文件在 Delphi 12.3 里打开会报前向声明缺失或者某个单元版本不一致这时候不要急着改代码先确认三点包文件是不是匹配 12.3 的版本源码路径是否在列表的最前面避免和其他版本的单元冲突有没有在 64 位平台上重复编译。第一次编译建议先编译运行时包再编译设计时包顺序反了会提示找不到dcu。编译通过后打开一个新的 VCL 工程组件面板上就能看到一个 EmbeddedWB 控件了通常出现在名为EmbeddedWB或Bsalsa的标签页下。2.4 安装后的验证拖一个 EmbeddedWB 到窗体上检查这几项属性面板里能看到URL、Silent、RegisterAsBrowser等定制属性说明设计时包安装成功。双击控件能进入OnDocumentComplete事件说明事件封装正确。运行一个空窗体直接在属性里设置URL : about:blank看是否能正常显示空白页。我之前遇到过一次控件拖上去了但事件列表几乎是空的后来发现是源码路径里同时存在两个版本的 EmbeddedWB 单元IDE 加载了旧版。把旧版本从 Library path 里去掉之后问题消失。如果你也装了多个版本建议用前面说的独立目录管理避免这类问题。3. 核心功能与实操细节3.1 基础导航与加载控制先从一个最基础的页面打开逻辑说起。实际上EmbeddedWB 的Navigate和 TWebBrowser 的用法非常接近但有几个重载参数procedure TForm1.LoadPage(const AUrl: string); begin EmbeddedWB1.Navigate(AUrl); end;如果想控制加载过程中的行为重点在事件上。OnBeforeNavigate2在每次导航发生前触发可以用它来做域名白名单比如只允许内网域名访问其他的一律拦截procedure TForm1.EmbeddedWB1BeforeNavigate2(ASender: TObject; const pDisp: IDispatch; const URL: Variant; const Flags: Variant; const TargetFrameName: Variant; var Cancel: WordBool); begin if Pos(example.com, URL) 0 then Cancel : True; end;这里要特别提醒一个坑页面里的 iframe 也会触发OnBeforeNavigate2和OnDocumentComplete如果你在OnDocumentComplete里拿EmbeddedWB1.Document做页面初始化很可能会在 iframe 加载完成时被多次触发。判断主框架是否加载完成可以看传入的pDisp是否等于当前文档的 parentWindowprocedure TForm1.EmbeddedWB1DocumentComplete(ASender: TObject; const pDisp: IDispatch; const URL: Variant); var Doc: IHTMLDocument2; begin if pDisp (EmbeddedWB1.Document as IHTMLDocument2).parentWindow then begin // 主页面加载完成在这里初始化 DOM 操作 end; end;这个判断方法我每次都用能省掉很多重复逻辑触发的麻烦。3.2 DOM 操作读取和修改网页内容网页加载完成之后真正高频的需求就是读数据、填数据。EmbeddedWB 简化了获取文档的过程只需要引入MSHTML单元然后通过EmbeddedWB1.Document拿到IHTMLDocument再转换成需要的接口。uses MSHTML; procedure TForm1.FillLoginForm; var Doc: IHTMLDocument3; UserInput: IHTMLElement; begin if EmbeddedWB1.Document nil then Exit; Doc : EmbeddedWB1.Document as IHTMLDocument3; // 通过 getElementById 定位元素 UserInput : Doc.getElementById(username); if UserInput nil then UserInput.setAttribute(value, admin); end;IHTMLDocument3提供了getElementById和getElementsByTagName已经能覆盖九成业务场景。如果需要更灵活的选择器IE 内核在较新的文档模式下也支持querySelector可以用IHTMLDocument3获取后再尝试调用IHTMLDocument3.querySelector。读取内容的思路也类似比如读表格数据var Doc: IHTMLDocument3; Trs: IHTMLElementCollection; Tr: IHTMLElement; i: Integer; begin Doc : EmbeddedWB1.Document as IHTMLDocument3; Trs : Doc.getElementsByTagName(tr); for i : 0 to Trs.length - 1 do begin Tr : Trs.item(i, EmptyParam) as IHTMLElement; // 在这里解析 Tr.innerText 或者继续取 td end; end;一个实践心得是对页面元素进行操作前先确认元素确实存在。因为内网页面不同业务分支可能渲染出不同结构代码里频繁出现 nil 判断总比运行时访问违例要好。3.3 执行 JavaScript 与实现页面回调 Delphi有时候要调用的函数暴露在页面的 JS 里用 DOM 操作模拟点击不够优雅直接执行 JS 更快。var Win: IHTMLWindow2; begin Win : (EmbeddedWB1.Document as IHTMLDocument2).parentWindow; Win.execScript(document.getElementById(btnSave).click(), JavaScript); end;execScript的第二个参数传JavaScript注意是字符串。这个函数执行 JS 的效率一般但胜在简单直接适合表单提交前的辅助逻辑。反过来让页面 JS 调用 Delphi 方法这是不少项目里比较绕的需求。常见实现思路是在 Delphi 端写一个实现IDispatch的 COM 对象暴露一个方法比如SaveData(const AData: WideString)。通过 EmbeddedWB 提供的接口把该对象设置为页面可访问的window.external。页面里直接调用window.external.SaveData(hello);。EmbeddedWB 把第二步封装得比原始 TWebBrowser 方便你可以设计期或运行时指定外部对象。实现完 COM 对象后记得在释放控件前把引用置空避免页面残留引用导致程序退出时报内存访问错误。另一个不推荐但常见的老派做法是用定时器轮询页面 DOM 某个“暗号”元素Delphi 端定时读这个元素的值一旦发现变化就触发回调。这种方式能绕过 COM 接口但效率低且容易误判一般只在页面完全不受控、无法改网页源码的极端情况下用。3.4 弹窗、新窗口、脚本错误与下载控制内嵌浏览器最怕的不是页面打不开而是各种弹窗干扰操作流程。脚本错误弹窗最直接的办法是设置EmbeddedWB1.Silent : True;同时把ShowErrorDialogs设为 False。这样一来大部分 JS 错误不会打扰到用户。新窗口拦截是另一个高频场景。默认点击target_blank链接会唤起外部浏览器体验割裂。处理方式是配合OnNewWindow2和OnBeforeNavigate2在OnNewWindow2里把新的导航导向当前控件。procedure TForm1.EmbeddedWB1NewWindow2(ASender: TObject; var ppDisp: IDispatch; var Cancel: WordBool); begin // 让新窗口在当前控件中打开 ppDisp : EmbeddedWB1.DefaultDispatch; Cancel : False; end;有时候页面会使用window.open并修改opener这部分逻辑在 IE 内核下比 Chromium 简单基本上上述拦截就能覆盖大部分场景。如果确实要禁止弹窗或跳转到外部在OnBeforeNavigate2里做域名判断最直接。下载操作在 IE 内核里一般会触发系统下载窗口。需要在项目里自主处理下载的话可以通过拦截OnDownload或者处理相关 COM 事件来实现这里不展开但提醒一句老版本 EmbeddedWB 在 Windows 10/11 上处理下载时可能会有兼容性差异实测下来走系统默认下载行为最稳。3.5 IE 兼容模式与注册表设置老业务系统免不了用 IE 的怪癖模式而 Windows 10/11 上 IE 内核默认的文档模式经常不够用。解决办法是针对当前程序设置浏览器仿真Browser Emulation。在程序启动时写注册表HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION在这个项下创建一个值名称是你的 EXE 文件名比如MyApp.exe值设置为十进制的11001即强制使用 IE11 标准模式。uses System.Win.Registry; procedure SetIECompatible; var Reg: TRegistry; begin Reg : TRegistry.Create; try Reg.RootKey : HKEY_CURRENT_USER; if Reg.OpenKey(Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION, True) then begin Reg.WriteInteger(MyApp.exe, 11001); Reg.CloseKey; end; finally Reg.Free; end; end;常用值说明值文档模式8000IE8 标准模式9000IE9 标准模式10001IE10 标准模式11001IE11 标准模式这个操作只对当前用户的当前程序生效不需要修改系统级 IE 设置风险可控。需要提醒的是文档模式调太高可能让一些老页面布局错乱调太低又可能不支持新的 JS 语法建议针对具体页面多做测试。4. 常见问题与排查技巧下面把这次用下来遇到最多的问题整理成速查表后面再做展开。问题现象可能原因处理思路控件在组件面板里找不到设计时包未安装成功或版本不匹配重新编译 dpk 并安装检查源码路径编译报找不到单元Library path 未配置或平台不匹配确认 32/64 位路径都加了源码目录打开页面白屏文档模式与页面不兼容调整 FEATURE_BROWSER_EMULATION 注册表值脚本错误弹窗频繁Silent 属性默认关闭设置 Silent 为 TrueShowErrorDialogs 为 False点击链接变成外部 IE 窗口新窗口拦截未处理在 OnNewWindow2 中设置 ppDisp页面加载完事件重复触发iframe 加载也触发 DocumentComplete判断 pDisp 是否为主窗口与 FireMonkey 工程不兼容控件是 VCL 实现VCL 工程用FMX 需另选方案4.1 控件在 IDE 中不定型或编译报错这个问题百分之八十是路径顺序问题。Delphi 的 Library path 是顺序查找的如果电脑上装了旧版本路径更靠前IDE 加载旧单元就会导致属性缺失、方法找不到。解决办法是把自己当前使用的 EmbeddedWB 源码目录放在最前面。还有一次我遇到一个很隐蔽的问题包文件编译时选的是 Win32 平台但 IDE 当前激活的是 Win64安装后组件面板显示不出来。后来手动切到 Win64 再编译一次包问题就消失了。4.2 页面白屏或渲染异常页面能导航但显示空白大概率是文档模式问题。IE 内核的默认文档模式在老系统里比较保守页面用了flex布局或者 ES6 语法就会空白。建议先在注册表里设置11001试试如果页面反而乱了再往下调。另外如果页面本身有控制台报错可以在OnScriptError里把错误信息打出来排查看procedure TForm1.EmbeddedWB1ScriptError(ASender: TObject; const ErrorLine: Integer; const ErrorCharacter: Integer; const ErrorMessage, ErrorCode, ErrorUrl: string; var ShowDialog: Boolean); begin LogPageError(Format(Line: %d, Message: %s, URL: %s, [ErrorLine, ErrorMessage, ErrorUrl])); ShowDialog : False; end;这个事件在排查页面适配问题时价值很高。4.3 FireMonkey 和移动端 PDA 场景有一个问题在搜索热词里反复出现就是 “Delphi FireMonkey PDA 扫码结果接收”。这里必须说明白EmbeddedWB 是 VCL 控件依赖 WinAPI 和 IE COM 接口不能直接用在 FireMonkeyFMX工程里更不能跨平台跑到 Android 或 iOS 上。如果你做的是 Windows 下的 FMX 程序想嵌网页还是得另想办法要么用FMX.Platform.Win下嵌入 Win32 窗口要么考虑 WebView 的 FMX 封装。做 Android PDA 扫码页面那就更不要考虑这个控件了应该走 FMX 原生浏览器方案或者自绘 WebView。4.4 长时间运行内存增长IE 内核的控件都有这个通病长时间高频切换页面内存会缓慢上涨。排查下来多数是因为外部代码持有了IHTMLDocument、IHTMLElement接口没有释放。Delphi 的 COM 接口变量是自动引用计数的但如果你把接口对象塞进了全局变量或对象成员里页面跳转后没有置空原网页的 DOM 就不会被释放。一个可行的缓解方案是每次页面跳转前把持有的页面相关接口变量统一置为 nil再去导航。另外避免在单页面上长期挂事件监听能卸载的事件尽量在OnBeforeNavigate2里解绑。5. 写在最后的实操心得折腾完这一圈我的一个直观感受是在 Delphi 生态里EmbeddedWB 这类老控件并没有完全过时它只是把“IE 内核上的 VCL 开发”这件事做到了极致。如果你的项目场景是内网系统、本地报表、表单自动化它依然能打而且迁移成本极低。最后分享一个实用的组合拳我一般会在OnBeforeNavigate2里做一次统一的前置拦截记录所有导航行为在OnDocumentComplete里判断主框架后集中执行 DOM 初始化和 JS 注入在OnScriptError里把错误输出到日志。三个事件配合好绝大多数嵌入页面的问题都能在第一时间定位到原因。如果你现在也正在 Delphi 12.3 上迁移老系统可以按这个思路先把框架搭起来遇到具体问题再逐个击破。本文还有配套的精品资源点击获取
返回列表