Winform 下 CefSharp 的引用, 配置, 实例与报错排除
[TOC]
1, 关于 CefSharp
装一手, 比较简单的英语
- It is a lightweight .NET wrapper around the Chromium Embedded Framework (CEF) by Marshall A. Greenblatt. About 30% of the bindings are written in C++/CLI with the majority of code here is C#. It can be used from C# or VB, or any other CLR language. CefSharp provides both WPF and WinForms web browser control implementations.
- CefSharp is BSD licensed, so it can be used in both proprietary and free/open source applications. For the full details, see the LICENSE file.
自己总结的:
CefShar 是一个提供了 Chromium Embedded Framework (CEF) 的. NET 接口的开源项目, 提供了 Winform,WPF 封装, 可以用来代替微软的 WebBrowser, 浏览网页, 尤为强大的是实现了 C#,VB 等 vs 支持的语言与 JavaScript 的交互方法.
2,CefSharp 项目源码下载
源码下载 目前最新 v53.0.1 https://github.com/cefsharp/CefSharp
源码结构介绍 http://www.h3399.cn/201805/577286.html
3,Winfrom 项目引入 CefSharp
我的开发环境 vs2013,.net4.0,Nugget 中搜索 CefSharp 显示的版本是 v53, 然而
v51.0.0-pre01
的 Breaking Changes 里面有这么一句
不想安装. net4.5.2, 所以只能通过
工具 - 程序包管理器 - 程序包管理控制台
手动命令行导入版本 v49.0.1 的, 输入命令
Install-Package CefSharp.WinForms -Version 49.0.1
回车等待执行完成, 我这里已经安装过了.
其他版本命令请参考.
等待导入成功, 生成一下... 报错...
嗯?!!!
如果你以为只是在
项目名右击 - 属性 - 生成 - 目标平台
改为 x86 就太天真了.... 反正我试了不行... 正确的姿 zhi 势 shi:
解决方案名右击 - 属性 - 配置属性 - 配置
, 右边平台选择 x86 或 x64, 什么? 选不到?
点击当前界面右上角
配置管理器 - 活动解决方案平台
下拉
新建
,x86/x64 随便选, 回来这边下拉已经可以选择了, 完事左下角确定, 再生成一下试试吧
以上看不懂的参考这里
4,Winfrom 下 CefSharp 的基本使用
4.1 显示一个页面
4.1.1 显示 url 网页
对照下面在一个 Form 的对应位置添加代码
- using CefSharp.WinForms;
- public partial class Form1 : Form
- {
- ChromiumWebBrowser webBrower = null;
- public Form1()
- {
- InitializeComponent();
- Load += Form_Load;
- }
- private void Form_Load(object sender, EventArgs e)
- {
- string path = "www.baidu.com";
- webBrower = new ChromiumWebBrowser(path);
- webBrower.Dock = DockStyle.Fill;// 填充方式
- this.Controls.Add(webBrower);
- }
- }
直接运行就 OK 了
4.1.2 显示一个本地 html
把 path 改为 File 协议就行, 例如: 显示程序下的文件夹 html 中的 test.html
- // 获取文件的物理路径
- string path = AppDomain.CurrentDomain.BaseDirectory + "\\html\\test.html";
- // 转换为 File 协议路径
- path = "file://" + path.Replace("\\", "/");
如果实在不知道怎么写的话, 建议直接用浏览器把 html 打开, 地址栏里面显示的 file:// 链接就是 File 协议路径
推荐个 CCS3 动画的 html, 直接右击保存到本地就行了, 修改完成, 同样运行就可以看到页面了, 运行还是挺流畅的.
4.2 JavaScript 调用异步 C# 方法
走弯路后的说明: BoundObject 等这三个类以及里面的方法都是可以自己任意定义实现的, 和普通的方法类没有太大区别, 没有必要去源码中拷过来.
从源码的
CefSharp.Example
中找到三个类 BoundObject,SubBoundObject,ExceptionTestBoundObject 的源码拷过来, 记得对应着自己的项目把命名空间名改了.
如果是. net4.0,TestCallback,TestCallbackException 两个方法有提示 async/await,Task.Run 的报错, 处理方式:
直接升级到. NET Framework 4.5: 从用户安装便捷性上讲, 个人感觉完全没必要
直接修改掉, 因为这是测试的代码, 可自己写不用 Task.Run, 详情请参阅, ; 如果是想在. net4.0 下使用 async/await 还有别的方法 - 直接 Nuget 搜索安装 Microsoft.Bcl.Async, 如果搜索太慢或者搜索不到可以直接在 nuget 控制台执行
Install-Package Microsoft.Bcl.Async
. 如果和我一样提示 Nuget 版本 2.7 太低, 至少需要 2.8 的, 需要卸载升级 Nuget, 步骤:
卸载: 打开
VS-打开菜单 "工具"-"扩展管理器"-选择 "NuGet Package Manager"-点击 "卸载"
, 然后会提示重启, 不自动重启的话可以自己手动重启
下载新版本 nuget{aa7aa}, 根据你的 vs 版本选择下载后缀是. vsix, 直接安装就行, 装完最好再重启一下 vs.
我这里选择不升级. net 4.5 后面一种方法, 同时安装 Microsoft.Bcl.Async.
这里实现的是一个延时回调的例子, 即点一个按钮, 调用绑定的 C# 方法在 3 秒后显示一条消息同时调用 js 方法立即显示一条消息.
测试内容准备:
BoundObject 中定义回调方法 (可以自己定义一个空的 BoundObject 类, 增加一下内容)
- public void TestCallback(IJavascriptCallback javascriptCallback)
- {
- //.net 4.0 的写法
- const int taskDelay = 3000;
- Task.Factory.StartNew(async () =>
- {
- Delay(taskDelay);
- using (javascriptCallback)
- {
- //var response = new CallbackResponseStruct("This callback from C# was delayed" + taskDelay + "ms");
- await javascriptCallback.ExecuteAsync("来自 C# 的返回值, 在当前延迟" + taskDelay + "ms");
- }
- });
- /*//.net 4.5 的写法
- const int taskDelay = 1500;
- Task.Run(async () =>
- {
- await Task.Delay(taskDelay);
- using (javascriptCallback)
- {
- //NOTE: Classes are not supported, simple structs are
- var response = new CallbackResponseStruct("This callback from C# was delayed" + taskDelay + "ms");
- await javascriptCallback.ExecuteAsync(response);
- }
- });
- */
- }
在 Form 中给 ChromiumWebBrowser 增加绑定, 第三个参数 false 忽略方法名大小写建议不要忘记, 否则 JS 中掉用时方法名默认区分大小写
webBrower.RegisterJsObject("bound", new BoundObject(),false);
新建个 html,body 中录入一下内容
- <p>
- Javascript Callback Test
- </p>
- <script type="text/javascript">
- function callback(s) {
- var result = document.getElementById('show');
- result.innerText += "callback 返回值:" + s.toString() + "时间:" + Date();
- }
- function TestCallback() {
- // 调用后台 C# 方法 TestCallback, 返回结果回调方法 callback
- bound.TestCallback(callback);
- var result = document.getElementById('show');
- result.innerText = "方法返回 时间:" + Date() + "\n";
- //window.location.assign("http://www.baidu.com");
- }
- </script>
- <button onclick="TestCallback()">
- TestCallBakck
- </button>
- <br />
- <span id="show">
- </span>
可以运行一下了, 点击一下
TestCallBakck
按钮, 会首先显示一行内容与时间, 3 秒后显示第二行内容.
4.3 JavaScript 调用带参数 C# 方法
准备内容:
BoundObject 添加 Repeat 方法
- public string Repeat(string str, int n)
- {
- string result = String.Empty;
- for (int i = 0; i <n; i++)
- {
- result += str;
- }
- return result;
- }
html 内容
<p>
带参数调用 C# repeat 方法
- </p>
- <span id="content">
- </span>
- <script type="text/javascript">
- var result = bound.Repeat("hello", 6);
- document.getElementById("content").innerHTML = ""+ result +"";
- </script>
执行结果
4.4 JavaScript 调用委托 C# 方法
这里将一个方法 ReturnJsonEmployeeList 作为参数进行传递, 并返回一个 json 数据进行解析展示
BoundObject 中添加方法
- public string ReturnJsonEmployeeList()
- {
- return "{\"employees\":[{\"firstName\":\"John\", \"lastName\":\"Doe\"},{\"firstName\":\"Anna\", \"lastName\":\"Smith\"},{\"firstName\":\"Peter\", \"lastName\":\"Jones\"}]}";
- }
html 内容
<p>
委托 C# 方法 - 返回 json 字符串
- </p>
- <script type="text/javascript">
- function myfunc(fucPara) {
- return fucPara();
- }
- var res = myfunc(bound.ReturnJsonEmployeeList);
- document.write("委托输出结果:" + res + "<br>");
- var json = JSON.parse(res); //eval("'"+ res+"'");
- document.write("employees 数量:" + json.employees.length + "<br>");
- document.write("json.employees[0].lastName :" + json.employees[0].lastName);
- </script>
运行结果:
4.5 JavaScript 调用 C# 返回实体对象
准备内容
新建类 SubBoundObject, 对照添加以下内容
- public class SubBoundObject
- {
- public string SimpleProperty { get; set; }
- public SubBoundObject()
- {
- SimpleProperty = "This is a very simple property.";
- }
- public string GetMyType()
- {
- return "My Type is" + GetType();
- }
- public string EchoSimpleProperty()
- {
- return SimpleProperty;
- }
- }
BoundObject 中添加内容
- public SubBoundObject SubObject {
- get;
- set;
- }
- public SubBoundObject GetSubObject() {
- return SubObject;
- }
html 中添加内容
<p>
返回实体对象
- </p>
- <script type="text/javascript">
- document.write("bound.GetSubObject().SimpleProperty :" + bound.GetSubObject().SimpleProperty);
- </script>
运行结果:
4.6 ChromiumWebBrowser 控件扩展 IContextMenuHandler 接口实现禁用右击按钮菜单
禁用右击菜单需要创建一个类实现接口 IContextMenuHandler, 然后把这个类赋值给 ChromiumWebBrowser 的 MenuHandler 即可. 这里创建类 MenuHandler:
- internal class MenuHandler : IContextMenuHandler
- {
- public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
- {
- model.Clear();
- }
- public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
- {
- return false;
- }
- public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame)
- {
- }
- public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
- {
- return false;
- }
- }
配置到 ChromiumWebBrowser:
webBrower.MenuHandler = new Class.MenuHandler();
这里只是禁用了菜单, 也可以在相关事件里面增加内容, 实现自定义右击菜单, 不同页面的不同右击菜单.
4.6 WebGL 页面渲染
使用 WebGL 可以实现很炫的页面效果, 以下这些实例都可以直接应用到一个引入了 CefSharp 的 WinFrom 桌面程序上面.
5, 依赖项
5.1 必需的运行环境
CefSharp 官方文件 readme.txt 中有以下要求:
根据 X86/X64 安装对应的
安装. Net 4.0 或者. Net 4.0 Client Profile
必有的依赖项
- libcef.dll (CEF code)
- icudtl.dat (Unicode Support data)
- CefSharp.Core.dll, CefSharp.dll,
- CefSharp.BrowserSubprocess.exe, CefSharp.BrowserSubProcess.Core.dll
根据不同的开发平台 CefSharp.WinForms.dll,CefSharp.Wpf.dll,CefSharp.OffScreen.dll 至少有一个
5.2 依赖内容排错
重要说明: 不想看高 (dan) 达 (dan) 上 (teng) 分析的, 请直接看 5.3 依赖列表
因为我这是 nuget 引入的, debug/release 生成的内容很多, 直接运行也是成功的, 因此我以发布为例, 实际和 debug/release 下的调试是一样的.
首先发布好的内容是这样的
直接运行
setup.exe
会有很多报错, 因为 vs 的发布过程中忽略很多依赖内容, 下面介绍一下怎么一步步定位到缺少的依赖项:
运行生成的 exe, 会报错这个
遇到这个问题的肯定没好好看上边的内容, 从源码或者 debug/release 下找到 libcef.dll,icudtl.dat 考到 exe 目录下这个错误就没有了
运行 exe 会有新的问题,
xxx 已停止工作 - 点击关闭程序
, 注意目录下出现了一个
debug.log
, 打开显示
FATAL:v8_initializer.cc(155)] Failed to open V8 file 'D:\Project\2013\CefSharpDemo\CefSharpDemo \ 发布 \ Application Files\CefSharpDemo_1_0_0_7\natives_blob.bin' (reason: -4)
说明缺少
natives_blob.bin
, 同上边直接拷贝过来
继续运行 exe,form1 出现了但是没有内容, 这时候看 log 是没有新的报错的, 但是请持续观察, 报错需要等一会儿才会进来
- [1226/142128:WARNING:resource_bundle.cc(311)] locale_file_path.empty() for locale
- [1226/142128:ERROR:main_delegate.cc(724)] Could not load locale pak for en-US
- [1226/142128:ERROR:main_delegate.cc(731)] Could not load cef.pak
- [1226/142128:ERROR:main_delegate.cc(748)] Could not load cef_100_percent.pak
- [1226/142128:ERROR:main_delegate.cc(766)] Could not load cef_extensions.pak
根据报错加入
locales\en-US.pak , cef.pak,cef_100_percent.pak,cef_extensions.pak
继续运行 exe,form1 出现了但是没有内容, 仔细研究日志
- [1226/142128:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/142129:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/142925:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/142926:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/143326:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/143327:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/143502:ERROR:child_process_launcher.cc(443)] Failed to launch child process
- [1226/143503:ERROR:child_process_launcher.cc(443)] Failed to launch child process
看懂了吗, 没看懂?
好吧, 我也没看懂.... 只好把 debug/release 下的文件往这边粘贴然后试试, 然后在粘了
CefSharp.BrowserSubprocess.Core.dll CefSharp.BrowserSubprocess.exe
两个之后奇迹发生了.... 抓紧把之前粘的全删了...
现在已经正常了, 但是, 但是 log 里面还有报错
- [1226/145245:ERROR:gpu_child_thread.cc(260)] Exiting GPU process due to errors during initialization
- [1226/145245:ERROR:browser_gpu_channel_host_factory.cc(133)] Failed to launch GPU process.
好了不废话了, 经过我的九牛二虎之力, 发现加入
libEGL.dll,libGLESv2.dll,d3dcompiler_43.dll,d3dcompiler_47.dll
就 OK 了
5.3 依赖文件列表
总结一下, CefSharp 必有的文件: ---locales
- | |--en-US.pak
- |--cef.pak
- |--cef_100_percent.pak
- |--cef_extensions.pak
- |--CefSharp.BrowserSubprocess.Core.dll
- |--CefSharp.BrowserSubprocess.exe
- |--CefSharp.Core.dll
- |--CefSharp.dll
- |--CefSharp.WinForms.dll
- |--d3dcompiler_43.dll
- |--d3dcompiler_47.dll
- |--icudtl.dat
- |--libcef.dll
- |--libEGL.dll
- |--libGLESv2.dll
- |--natives_blob.bin
补充一下, 运行环境 vs2013,.NET4.0,Winfrom,CefSharp v40.0.1.
6, 总结
根据今天初步的部署, 测试, 感觉 CefSharp 可以算是比较成熟的 CEF 的在. net 下的实现了, JavaScript 和 C# 的交互做的简单易用, 感觉可以比较容易的将一个 Web 应用的资源经过一定的重新整合打造成为一个桌面程序.
吐槽: 一个字大, 因为从根本上是集成了一个 chrome 浏览器, 所以以上简单的 Demo 的发布程序的 x64,release 版本就达到了 99.5M,360 压缩, 7z 格式压缩后 30.6m, 正式项目增加了代码, 引入了其他的 dll, 资源文件等会更大; 不知道有没有方法可以做精简
以上均为个人看法, 各路大神请指点
来源: