ARTICLE DETAIL

资讯详情

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

Delphi开发必备:EmbeddedWB浏览器控件从安装到深度定制

Delphi开发必备:EmbeddedWB浏览器控件从安装到深度定制 简介面向Delphi开发者特别是基于Delphi 12.3环境需要集成网页浏览能力的项目团队这份EmbeddedWB控件完整源代码包提供了从底层封装到上层调用的参考实现。压缩包共226个文件包含65个pas源文件、126个dcu编译单元和22个dpk包工程文件辅以res、inc、dproj等工程辅助文件整体仅2.44MB结构紧凑可直接在对应版本中编译与调试。已有83位开发者学习或浏览此资源适合对WebBrowser内核封装感兴趣的进阶用户。源码覆盖了EmbeddedWB主控件、EwbTools工具集、RichEditBrowser富文本浏览以及IEDownload下载辅助等模块深入阅读可以理清控件如何基于ActiveX技术解析网页、触发DOM事件并直接进入单元文件查看具体实现开发者可按需定制接口、优化加载逻辑或将其迁移到不同版本的Delphi工程中。这份代码无论用于学习浏览器组件原理还是作为桌面应用扩展网页功能的起点都是非常有价值的参考。1. EmbeddedWB一份从 D5 到 Delphi 12.3 的 IE 内核控件源码包做 Delphi 桌面开发用过原生 TWebBrowser 的基本都有种“土埋半截”的感觉事件不完整想截个右键菜单得写一堆 COM 接口页面加载状态还经常和 UI 线程纠缠。这篇笔记要拆的 EmbeddedWB是一套从 Delphi 5 一路支持到 Delphi 12.3 的 IE 内核浏览器控件源码压缩包名字就叫 EmbeddedWB-D5-XE12-SRC.7z里面全是 .pas、.dfm、.dpr 源文件没有编译好的二进制反倒方便你按自己的 IDE 版本重新编译。它能解决的核心问题是把 IE 那一大坨 COM 接口封装成组件、属性、事件让 Delphi 开发者像操作普通控件一样去处理浏览器行为加载字符串、注入脚本、拦截弹出窗、自定义右键菜单、甚至接管页面里的外部对象。适合的读者很明确做 Delphi ERP、进销存、OA 这类“壳里嵌网页”项目的以及那些因为 TWebBrowser 不够顺手而到处找替代方案的工程师。2. 从 .7z 源码包到 IDE 工具栏编译安装的完整流程2.1 D5-XE12 的版本映射与源码目录结构EmbeddedWB 的源码包和很多老控件一样喜欢按 Delphi 编译器版本分目录。你解压后会看到类似Delphi5、Delphi6、Delphi7、D2007、XE、XE10之类的文件夹每个文件夹里放着适合那个版本编译的.dpk工程文件和对应源码。后面出的版本可能不再按“版本号”划分而是用通用源码加条件编译来适配不同编译器这一点在 Delphi 12 里尤其明显——IDE 对 ANSI 字符串的处理策略变了老源码直接用经常会出问题。拿到压缩包第一件事不是双击 dpk而是先建一个干净的目录比如D:\Libs\EmbeddedWB把EmbeddedWB-D5-XE12-SRC.7z解压进去。我一般会用 7-Zip 的命令行避免中文路径和长路径带来的麻烦7z x EmbeddedWB-D5-XE12-SRC.7z -oD:\Libs\EmbeddedWB这个命令把压缩包解压到指定目录-o后面紧跟目标路径。解压后先别急着开 IDE先在资源管理器里看一遍顶层结构重点找两个东西一是根据你的 Delphi 版本对应的.dpk文件比如EmbeddedWB.dpk二是Source目录下的一堆.pas文件。很多老项目会把源码直接放在跟版本文件夹同级的src下所以先dir /s /b *.dpk搜一下更稳dir /s /b *.dpk如果搜出多个 dpk优先选名字带“Design”或“RunTime”字样那个。通常 EmbeddedWB 的包分设计期包和运行期包设计期包用于往 IDE 组件面板上注册运行期包供工程链接。常规做法是编译并安装设计期包运行期包会作为依赖自动编译。2.2 在 Delphi 12.3 中添加 Library 路径源码目录加进 IDE 的全局搜索路径这一步不做后面打开 dpk 时会到处报找不到.pas。Delphi 12 的菜单路径是Tools Options Language Delphi Library在Library path里把刚才源码所在的目录加进去建议同时加上Source子目录。这一步有讲究不能只加顶层目录因为很多源码文件被放在Source\Common、Source\Tools这种二级目录里。我一般会添加这么几项路径用途D:\Libs\EmbeddedWB顶层单元引用D:\Libs\EmbeddedWB\Source主源码D:\Libs\EmbeddedWB\Source\Tools附带工具单元如果用的是 XE 系列Options Delphi Library路径同样处理。之后打开设计期包文件例如EmbeddedWBD10.dpk不同版本文件名不同点击编译即可。编译通过后再点安装组件面板会出现EmbeddedWB一栏。2.3 编译顺序从底层单元到顶层组件需要提醒的是包编译有依赖顺序。如果源码里有wbxWindows Browser X之类的基础类库要先把这些底层库编译成.bpl或者至少让 IDE 能找到它们的.pas。常见做法是先打开EmbeddedWB_R.dpk运行期包执行 Build再打开EmbeddedWB_D.dpk设计期包执行 Compile 然后 Install。顺序反了容易报Unit xxx was compiled with a different version of xxx。代码包里通常带的Demos例子很值得先跑一次比如SimpleBrowser示例它能验证控件是否真的编译通过。如果打开 Demo 工程后运行报错十有八九是 Library Path 没配全回头把Tools目录也加上再试。我自己在 Delphi 12.3 上编译时还遇到过Winapi.WinInet和System.Win.WinInet命名空间冲突的问题原因就是早期源码里有的单元直接uses WinInet而新版 IDE 里单元名变了解决办法是在工程文件里把Winapi.WinInet加到uses前面或者用条件编译隔离。2.4 用最小工程验证控件是否真的可用装完后往空的 Form 上拖一个TEmbeddedWB什么属性都不改直接运行。如果窗体上出现一个白色网页区域说明基础链路 OK。但这时候右键、双击都没反应因为默认配置下控件只实现了最基本的浏览功能。我习惯先设置两个基础属性Navigate(about:blank)作为初始页面ShellMode设为smEmbedded。procedure TForm1.FormCreate(Sender: TObject); begin EmbeddedWB1.Navigate(about:blank); EmbeddedWB1.ShellMode : smEmbedded; // 隐藏默认浏览器外壳 end;Navigate是继承自 TWebBrowser 的方法加载空页面ShellMode是 EmbeddedWB 扩展的枚举属性设为smEmbedded后控件不再显示 IE 的地址栏和状态栏适合做嵌入式页面。注意不同版本的枚举名可能不同有的用wfEmbedded编译时如果不认识就翻一翻源码里的定义。3. 核心能力把 IE 引擎揉成 Delphi 能控制的零件3.1 控件模型从 IWebBrowser2 到 TEmbeddedWBEmbeddedWB 本质上是在IWebBrowser2接口外面包了一层 Delphi 面向对象外壳。它保留了 IE 的 COM 原声能力又暴露了更高层的事件和属性。比如OnNavigateComplete2、OnDownloadComplete、OnScriptError这些事件写惯了TWebBrowser.OnTitleChange的人会觉得瞬间舒服。常用的几个功能入口如下功能属性/方法说明加载HTML字符串LoadFromString直接渲染字符串设置静默Silent : True禁止脚本错误弹窗隐藏右键菜单OnMenuCommand拦截自定义菜单回调页面文本访问Document属性通过IHTMLDocument23.2 在 Delphi 12.3 里加载 HTML 字符串代码写在内存中没有 EmbeddedWB 时想显示动态内容往往得先写临时文件再 Navigate多少有点多余。EmbeddedWB 可以直接把字符串塞给 IE 内核。常见的调用方式是procedure TForm1.Button1Click(Sender: TObject); var LHtml: string; begin LHtml : htmlheadtitletest/title/head body onloaddocument.titledocument.title\ ok\ scriptfunction Add(a,b){return ab;}/script pDelphi embedded/p/body/html; EmbeddedWB1.LoadFromString(LHtml); end;注意LoadFromString方法名在不同版本里略有差异早期版本叫LoadFromString后来有的版本改叫LoadHTMLString。我一般直接在源码里搜索procedure Load**String确认。加载后页面里的Add函数已经存在于IHTMLWindow2对象中后面可以通过execScript调用它。参数说明LoadFromString本质上会调用IWebBrowser2.Navigate(about:blank)然后用IPersistStream或IHTMLDocument2.write把字符串写入文档流。所以如果你传入的字符串是 UTF-8 编码但没声明meta charset中文大概率会乱码。解决的办法是构造字符串时在 head 里带上meta http-equivContent-Type contenttext/html; charsetutf-8或者提前把字符串转成宽字符再传入。3.3 调用网页里的脚本函数并接收返回值很多时候不只是让页面显示出来还要反调脚本。Delphi 端通过IPersistStream初始化文档后可以直接拿到IHTMLWindow2然后调用execScript执行脚本再通过IHTMLDocument2.body.innerText拿到结果。但这样往返 COM 非常繁琐所以更干净的方式是使用 EmbeddedWB 暴露的Document和Script接口。常见的做法是procedure TForm1.Button2Click(Sender: TObject); var Result: OleVariant; begin EmbeddedWB1.ExecScript(Add(3,4), JavaScript, Result); ShowMessage(Result); end;ExecScript是我封装的一层函数内部调用IHTMLWindow2.execScript把脚本执行结果放到Result变量中。第二个参数JavaScript指定脚本语言第三个参数为返回的变体值。如果脚本有语法错误Result会是 Variant 的Null或Empty同时Silent为 False 时还会弹出确认框。所以我通常会在初始化时把Silent设为 True再配合OnScriptError事件把错误记到日志文件里而不是让用户看到“脚本错误”的刺眼对话框。如果你不想自己封装EmbeddedWB 的 Demo 里也自带了一个ExecScript示例可以直接抄。需要注意线程问题网页脚本在 IE 的线程里运行Delphi 侧的回调过来时不一定在主线程更新 UI 前要插一句TThread.Queue或者用Synchronize否则偶尔会遇到闪退。这一点老手也容易翻车。3.4 拦截弹窗和新建窗口接管浏览器生命周期网页里点了 target_blank 链接默认会启动一个新 IE 窗口这在嵌入式程序里非常讨厌。EmbeddedWB 提供OnNewWindow事件可以在事件里把目标 URL 传给当前窗口或者直接截掉。我一般是这么处理的procedure TForm1.EmbeddedWB1NewWindow(Sender: TObject; const URL: string; var Cancel: WordBool; var ViewSource: WordBool); begin Cancel : True; EmbeddedWB1.Navigate(URL); end;Cancel置为 True 时原始窗口不会再弹出新浏览器Navigate让当前控件加载新地址。注意ViewSource参数表示是否打开源代码窗口这个通常不需要动。拦截弹窗也是同理OnStatusCode或OnCommandStateChange里可以判断弹出窗口的调用来源对于广告弹出直接置 Cancel 即可。3.5 接管页面里的定时器和下载行为页面里的setTimeout和下载链接EmbeddedWB 都能通过事件反馈到 Delphi。比如OnDownload事件如果不想让 IE 直接下载文件可以在这里接管流。常见做法是先在事件里读取目标 URL然后用IdHTTP之类的库下载下载进度还能显示在进度条上procedure TForm1.EmbeddedWB1Download(Sender: TObject; const URL: string; var DownloadType: Integer; var Cancel: Boolean); begin if DownloadType 0 then // 0 表示主框架文档 begin // 这里写自定义下载逻辑比如显示进度条 end else Cancel : False; end;这样写的好处是把浏览器下载行为变成可编程步骤而不是黑匣子。注意DownloadType的枚举含义随版本可能不同最好查一下源码里的注释。4. 与 TWebBrowser 对比该换的时候别手软4.1 功能维度TWebBrowser 与 EmbeddedWB 的明显差异维度TWebBrowserEmbeddedWB事件粒度只有导航、标题等基础事件有下载、脚本错误、右键菜单、焦点、打印等几十个事件字符串加载要临时文件或 COM 写法有封装好的方法脚本调用需要自己拿 IHTMLWindow2有封装方法多线程基本不支持支持一定程度的线程回调版本兼容随着 IDE 自带需要自己编译适配TWebBrowser 对大多数简单展示就够了但如果你要在网页里实现“JS 调用 Delphi 函数”、“拦截新窗口”、“定制右键菜单”TWebBrowser 会让你写一大堆IDispatch和IWebBrowser2的模板代码而且不同 Windows 版本上行为还有差异。EmbeddedWB 把这些都收拢到组件事件里省的是真功夫。4.2 从 TWebBrowser 迁移老项目怎么动刀不要直接替换控件因为 TWebBrowser 没有的属性、事件会让代码编译不过。我是这么做的先在工程里把TWebBrowser替换为TEmbeddedWB然后把OnDownloadComplete之类的事件挂上对应的事件名再去掉原来手工封装的SetSilent函数。举个例子原来用 TWebBrowser 时为了不显示脚本错误框需要这么写uses SHDocVw, MSHTML, ActiveX; procedure SetWebBrowserSilent(const wb: TWebBrowser; const Silent: Boolean); var Opt: OleVariant; begin Opt : wb as IDispatch; try Opt.OlePropertySet(Silent, Silent); except end; end;换成 EmbeddedWB 后这条函数直接删掉因为组件自带Silent属性EmbeddedWB1.Silent : True;这种迁移虽然看起来简化但要注意事件参数类型不同。OnDownloadComplete在 TWebBrowser 里没有EmbeddedWB 里是有的可参数类型可能多一个DownloadType所以替换时别直接 Copy 旧事件处理逻辑先看事件声明的参数表。4.3 什么时候不用换简单静态展示别自找麻烦如果你的需求只是把公司官网塞进窗体不交互、不回调那 TWebBrowser 就行连EmbeddedWB都不用装。换了反而多一层维护成本。但只要你开始写“网页里点按钮要调 Delphi 方法”这类需求或者要处理脚本错误、要拦截下载那 TWebBrowser 的坑会越来越大这时候换成 EmbeddedWB 才是止损。判断标准我总结成一句话遇到第三个不兼容问题就开始迁移。第一个问题是事件缺第二个问题是字符串加载第三个问题是弹窗拦截基本就该换了。4.4 多浏览器实例ERP 界面同时显示多个网页区域ERP 界面经常要同时显示几个网页区域比如左边的 BI 报表和右边的表单。用 TWebBrowser 做三个实例内存占用和事件响应都会明显变差因为 TWebBrowser 每次创建都是一套完整的 IE 进程外通信。EmbeddedWB 因为是源码级封装理论上能减少一部分调度开销而且它支持将窗口句柄绑定到已有窗体更节省资源。如果遇到页面子控件间距异常比如输入框之间出现白缝多数是 IE 渲染模式不对需要在注册表里给程序设置FEATURE_BROWSER_EMULATION对应值典型的 8000 表示 IE8 模式11000 表示 IE11 模式。这一点对 TWebBrowser 同样适用但 EmbeddedWB 流行早网上针对它的设置案例更多。5. 避坑与排查我装完、跑起来以后踩过的五个坑5.1 编译期老源码在 Delphi 12.3 里报 ANSI 类型不匹配现象打开 dpk 编译报E2003 Declaration of Char differs from previous或者字符串常量警告。原因EmbeddedWB 早期版本针对 Delphi 5-7 开发大量用PChar直接指向AnsiString到了 Unicode 版本后PChar是 2 字节宽字符类型推断就乱了。解决在工程条件编译里加{$IFDEF UNICODE}块或者直接用源码里带的新版适配文件。我一般会把所有PChar调用点搜出来把不涉及 API 的换成AnsiString后再用PAnsiChar指向。在 Delphi 12.3 上建议优先使用源码包里明确写着支持 New Compiler 的版本分支。5.2 运行期双击页面变成了自定义对话框现象用户双击网页里的文本弹出一个灰底的小窗口而不是选中文本。原因EmbeddedWB 默认拦截了双击事件触发了它内置的OnMouseDown或OnDblClick。解决检查EmbeddedWB1.Events相关属性把DblClick处理设为edNone或者给事件处理器返回False。5.3 运行期关闭窗口 Access Violation现象主窗口关闭过程中报Access violation at address ... in module ...。原因网页在 onload 里还在执行异步脚本窗体释放时IWebBrowser2引用没断COM 回调对已释放的接口进行调用。解决在FormDestroy里先置EmbeddedWB1.Visible : False然后调用EmbeddedWB1.DisconnectAll或释放事件里面的服务器引用。我试过最稳的顺序先Navigate(about:blank)把页面清空再DisconnectAll最后Free。5.4 兼容性Win11 上页面呈现异常或子控件间距错乱现象同一代码在 Win10 正常Win11 显示网页内容偏右、有白边或者页面里 input 控件间距变大。原因系统浏览器内核升级后默认文档模式从 IE7 变成 IE11导致 CSS 尺寸计算变化。解决在注册表HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION下增加你的 exe 名值设为11000十进制。如果公司环境禁改注册表可以考虑在程序初始化时用InternetSetOption设置INTERNET_OPTION_SETTINGS_CHANGED不过不如注册表直接。5.5 编译期组件安装成功但拖不到窗体上现象安装包后组件面板出现了 EmbeddedWB但拖到窗体上鼠标变成禁止符号。原因设计期许可证或包注册信息缺失。解决确认安装的 dpk 是 Design 类型且Register函数里有RegisterComponents(EmbeddedWB, [TEmbeddedWB])这句。我遇到一次是因为先装了一个旧版本后又覆盖新版本组件类名冲突重启 IDE 或者彻底卸载旧包就解决了。5.6 黑匣子排查页面加载一半停住时先看哪里现象页面转圈OnDocumentComplete不触发。原因页面里有跨域资源或WebSocket挂在等待。解决在OnDownloadBegin里打日志记录 URL设置EmbeddedWB1.DownloadTimeout属性检查AllowJavaScript等开关。我用这个流程定位过好几次问题最后都是脚本里某个setInterval没清掉导致回调死循环把页面卡在加载状态。6. 右键菜单加自定义命令一个能直接落地的 EmbeddedWB 技巧EmbeddedWB 最值得学的就是右键菜单接管。常规做法是使用OnShowContextMenu事件来修改菜单结构。在事件里可以拿到IHTMLDocument2和点击元素然后用 WinAPI 的AppendMenu添加自定义项。不过更干净的方式是直接用TEmbeddedWB.Menu相关的属性自己重新生成全部菜单。我这里给一个简单写法在OnContainerMenu事件中拦截procedure TForm1.EmbeddedWB1ContainerMenu(Sender: TObject; const elem: IHTMLElement; const target: IDispatch; const menu: OLEVariant; var Handled: WordBool); var hMenu: HMENU; begin hMenu : Integer(menu); AppendMenu(hMenu, MF_STRING or MF_BYPOSITION, 40001, Delphi 追加项); Handled : False; // 让 IE 继续处理原菜单 end;然后在OnMenuCommand里响应procedure TForm1.EmbeddedWB1MenuCommand(Sender: TObject; const Command: Integer; const elem: IHTMLElement; const target: IDispatch; var Cancel: WordBool); begin if Command 40001 then begin ShowMessage(你点了 Delphi 追加项); Cancel : True; // 防止继续传给 IE end; end;注意menu参数用 OleVariant 接收在 XE 系列里可能是 LongWord直接Integer(menu)即可取到菜单句柄。AppendMenu的命令 ID 尽量用 40000 以上避免和 IE 内置命令撞。elem是对应的页面元素可以读取tagName、innerHTML等做动态菜单特别方便。这个技巧常用于 ERP 里的“把选中文本直接写入填单窗口”、“对图片右键保存到本地库”。从那次之后凡是给客户做带浏览器的界面我都强制要求把右键菜单先做成可控的避免 IE 原版菜单把“查看源码”漏给客户。希望这个示例帮你在自己的项目里少踩一次壳。希望帮到你。本文还有配套的精品资源点击获取
返回列表