Winform下CefSharp的引用、配置、實例與報錯排除
本文詳細介紹了CefSharp在vs2013、.net4.0環境下,創建Winfrom項目、引用CefSharp的方法,演示了winfrom下CefSharp的基本使用,包括顯示網頁url/本地html顯示、JavaScript調用異步C#方法、JavaScript調用帶參數C#方法、JavaScript調用委托C#方法、JavaScript調用C#返回實體對象、ChromiumWebBrowser控件擴展IContextMenuHandler接口實現禁用右擊按鈕菜單、簡單介紹WebGL頁面渲染;詳細介紹了的CefSharp依賴項、常見報錯查看與必要依賴項分析,最后進行了簡單總結、並提供了源碼的下載.
[TOC]
1、關於CefSharp
裝一手,比較簡單的英語
CefSharp lets you embed Chromium in .NET apps. 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項目源碼下載
- 源碼下載 github托管地址 目前最新v53.0.1
- 源碼結構介紹構建基於Chromium的應用程序 - 貓不理餅
3、Winfrom項目引入CefSharp
我的開發環境vs2013、.net4.0,Nugget中搜索CefSharp顯示的版本是v53,然而v51.0.0-pre01
的Breaking Changes里面有這么一句
CefSharp requires at least .Net 4.5.2 (Last version to support .Net 4.0 is 49)
不想安裝.net4.5.2,所以只能通過工具-程序包管理器-程序包管理控制台
手動命令行導入版本v49。0.1的,輸入命令Install-Package CefSharp.WinForms -Version 49.0.1
回車等待執行完成,我這里已經安裝過了。
其他版本命令請參考http://www.nuget.org/packages/CefSharp.WinForms.
等待導入成功,生成一下...報錯...
CefSharp.Common does not work correctly on 'AnyCPU' platform. You need to specify platform (x86 / x64).
嗯?!!!
cefsharp不支持anycup,還需要設置一下目標平台為x86或x64
如果你以為只是在項目名右擊-屬性-生成-目標平台
改為x86就太天真了....
反正我試了不行...
正確的姿zhi勢shi: 解決方案名右擊-屬性-配置屬性-配置
,右邊平台選擇x86或x64,什么?選不到?
點擊當前界面右上角配置管理器-活動解決方案平台
下拉新建
,x86/x64隨便選,回來這邊下拉已經可以選擇了,完事左下角確定,再生成一下試試吧
以上看不懂的參考這里.net使用cefsharp開源庫開發chrome瀏覽器(二)
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協議路徑
需要在 test.html文件上
右擊-屬性-復制到輸出目錄
改為始終復制
,這樣調試時debug下面才會有這個文件
推薦個CCS3動畫的html,直接右擊保存到本地就行了大風車CCS3動畫,修改完成,同樣運行就可以看到頁面了,運行還是挺流暢的.
4.2 JavaScript調用異步C#方法
走彎路后的說明:BoundObject等這三個類以及里面的方法都是可以自己任意定義實現的,和普通的方法類沒有太大區別,沒有必要去源碼中拷過來.
從源碼的CefSharp.Example
中找到三個類BoundObject、SubBoundObject、ExceptionTestBoundObject的源碼拷過來,記得對應着自己的項目把命名空間名改了。
如果是.net4.0,TestCallback、TestCallbackException兩個方法有提示async/await、Task.Run的報錯,處理方式:
- 直接升級到.NET Framework 4.5:從用戶安裝便捷性上講,個人感覺完全沒必要
- 直接修改掉,因為這是測試的代碼,可自己寫不用Task.Run,詳情請參閱CSDN論壇-Task.Factory.StartNew()和Task.Run()有什么區別、 CSDN論壇-task .run必須是 4.5框架,4.0里沒有;如果是想在.net4.0下使用async/await還有別的方法-直接Nuget搜索安裝Microsoft.Bcl.Async,如果搜索太慢或者搜索不到可以直接在nuget控制台執行
Install-Package Microsoft.Bcl.Async
.如果和我一樣提示Nuget版本2.7太低、至少需要2.8的,需要卸載升級Nuget,步驟:- 卸載:打開
VS-〉打開菜單“工具”-“擴展管理器”-〉選擇“NuGet Package Manager”-〉點擊“卸載”
,然后會提示重啟,不自動重啟的話可以自己手動重啟 - 下載新版本nugethttps://dist.nuget.org/index.html,根據你的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(全寫Web Graphics Library)是一種3D繪圖標准,這種繪圖技術標准允許把JavaScript和OpenGL ES 2.0結合在一起,通過增加OpenGL ES 2.0的一個JavaScript綁定,WebGL可以為HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就可以借助系統顯卡來在瀏覽器里更流暢地展示3D場景和模型了,還能創建復雜的導航和數據視覺化。顯然,WebGL技術標准免去了開發網頁專用渲染插件的麻煩,可被用於創建具有復雜3D結構的網站頁面,甚至可以用來設計3D網頁游戲等等。
使用WebGL可以實現很炫的頁面效果,以下這些實例都可以直接應用到一個引入了CefSharp的WinFrom桌面程序上面.
9個令人震驚的WebGL示例
20個使用WebGL和Three.js實現的網頁場景
5、依賴項
5.1必需的運行環境
CefSharp官方文件readme.txt中有以下要求:
- 根據X86/X64安裝對應的 Visual C++ Redistributable Packages for Visual Studio 2013
- 安裝.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、資源文件等會更大;不知道有沒有方法可以做精簡
以上均為個人看法,各路大神請指點
7、源碼下載與參考資料
7.1提供了兩個版本
CefSharpDemo20161227 包含packages 157M CefSharpDemo20161227 不含packages 131K
下載請去原文:http://huisky.com/blog/16122515111968