Chromium Embedded Framework (CEF)是個基於Google Chromium項目的開源Web browser控件,支持Windows, Linux, Mac平台。除了提供C/C++接口外,也有其他語言的移植版。
因為基於Chromium,所以CEF支持Webkit & Chrome中實現的HTML5的特性,並且在性能上面,也比較接近Chrome。
1.從Nuget下載CEF框架
從nuget下載是最簡單的:
我的示例程序是WPF程序,所以下載的框架是CefSharp.Wpf,你也可以下載Cef的其他版本如CefSharp.WinForms
2.創建一個Wpf窗口:
<Window x:Class="ChromeWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cefSharpWPF="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
mc:Ignorable="d"
Title="ChromeWindow" Height="450" Width="800" Closing="Window_Closing">
<DockPanel Name="dockpanel" LastChildFill="True">
<cefSharpWPF:ChromiumWebBrowser Name="mybrower" Address="https://www.cnblogs.com/tuyile006"></cefSharpWPF:ChromiumWebBrowser>
</DockPanel>
</Window>
打開看看效果。這是超簡單的使用方式,我就不展示了。
3.讓cef chromium支持flash視頻播放。
很多網頁中有flash,內嵌的chromium瀏覽器是不支持flash的,必須設置一下。
需要下載一個插件,放到項目中,並隨項目發布到輸出目錄。

主要看構造函數中的代碼:
using CefSharp;
using CefSharp.Wpf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
namespace xiaoy.Control
{
/// <summary>
/// 谷歌瀏覽器幫助類(靜態類)
/// </summary>
public class ChromeCefHelper
{
private static List<ChromeWindow> webList= new List<ChromeWindow>();
private static object lockObj = new object();
public static string ErrhtmlTemplate = string.Empty;
static ChromeCefHelper()
{
//初始化瀏覽器設置
if (!Cef.IsInitialized)
{
//打開靜態地址
string strMenu = AppDomain.CurrentDomain.BaseDirectory;
//pepflashplayerDLL 地址
string flashPath = strMenu + @"\flashPlugin\pepflashplayer64_32_0_0_414.dll";
CefSettings set = new CefSettings();
set.CachePath = strMenu + "\\cache";
set.PersistSessionCookies = true;
set.LogSeverity = LogSeverity.Disable;
//安全證書
set.CefCommandLineArgs.Add("--ignore-urlfetcher-cert-requests", "1");
set.CefCommandLineArgs.Add("--ignore-certificate-errors", "1");
//開啟ppapi-flash
set.CefCommandLineArgs["enable-system-flash"] = "1";
set.CefCommandLineArgs.Add("ppapi-flash-version", "32.0.0.414");
//插入地址
set.CefCommandLineArgs.Add("ppapi-flash-path", flashPath);
//訪問本地資源
set.RegisterScheme(new CefCustomScheme
{
SchemeName = CefSharpSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CefSharpSchemeHandlerFactory()
});
//啟用配置
//bool bint= CefSharp.Cef.Initialize(set);
Cef.Initialize(set, performDependencyCheck: false, browserProcessHandler: null);
//錯誤網頁模板
string htmlpath = AppDomain.CurrentDomain.BaseDirectory + "html\\error.html";
ErrhtmlTemplate = File.ReadAllText(htmlpath);
}
}
/// <summary>
/// 自動設置token到cookie中
/// </summary>
public static void SetCookies()
{
if (!string.IsNullOrEmpty(CommCach.Token))
{
lock(lockObj)
{
try
{
//獲取portalurl中的域名地址
int starti = CommCach.PortalUrl.IndexOf("//") + 2;
int endi = CommCach.PortalUrl.IndexOf("/", starti);
string domain = CommCach.PortalUrl.Substring(starti, endi - starti);
var cookieManager = CefSharp.Cef.GetGlobalCookieManager();
cookieManager.SetCookieAsync("http://" + domain, new CefSharp.Cookie()
{
Domain = domain,
Name = "Admin-Token",
Value = HttpUtility.UrlEncode(CommCach.Token),
Expires = DateTime.MinValue
});
}
catch(Exception ex)
{
LogHelper.Error("寫Cookie失敗",ex);
}
}
}
}
/// <summary>
/// 第一次啟動
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static ChromeWindow Open(string title,string url)
{
SetCookies();
ChromeWindow cw = new ChromeWindow();
cw.Title = title;
cw.Open(url);
cw.Show();
//cw.WindowStyle = WindowStyle.None;
webList.Add(cw); //放在緩存中 以便退出關閉瀏覽器
return cw;
}
/// <summary>
/// 在原chrome中改url
/// </summary>
/// <param name="webDriver"></param>
/// <param name="url"></param>
public static void Open(ChromeWindow webDriver, string title, string url)
{
if (webDriver != null)
{
SetCookies();
webDriver.Title = title;
webDriver.Open(url);
}
}
/// <summary>
/// 關閉所有窗口
/// </summary>
public static void CloseAll()
{
foreach (ChromeWindow w in webList)
{
if (w != null)
w.Close();
}
webList.Clear();
}
}
}
ChromeWindow.xaml是要彈的瀏覽器窗體。在ChromiumWebBrowser對象初始化完之后需要支持直接打開flash播放,不能讓用戶點播放按鈕才播放,這是用戶使用習慣。實現如下:
前端窗體可以只放個DockPanel控件,后台代碼動態添加瀏覽器控件
<Window x:Class="xiaoy.ChromeWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="ChromeWindow" Height="450" Width="800" Closing="Window_Closing">
<DockPanel Name="dockpanel" LastChildFill="True">
<!--<cefSharpWPF:ChromiumWebBrowser Name="mybrower" Address="https://www.cnblogs.com/tuyile006"></cefSharpWPF:ChromiumWebBrowser>-->
</DockPanel>
</Window>
然后在Browser_IsBrowserInitializedChanged中自動播放flash
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CefSharp;
using CefSharp.Wpf;
namespace xiaoy.Control
{
/// <summary>
/// ChromeWindow.xaml 的交互邏輯
/// </summary>
public partial class ChromeWindow : Window
{
ChromiumWebBrowser browser = null;
bool bInited = false;//是否初始化完成
public ChromeWindow()
{
InitializeComponent();
//初始化瀏覽器
InitBrowser();
//Open(url);
}
/// <summary>
/// 初始化瀏覽器,讓其支持flash
/// </summary>
/// <param name="url"></param>
public void InitBrowser()
{
browser = new ChromiumWebBrowser();
// browser.RequestHandler=new CustomRequestHandler();
browser.KeyboardHandler = new CustomKeyBoardHander();
BrowserSettings bset = new BrowserSettings();
bset.Plugins = CefState.Enabled;
bset.ApplicationCache = CefState.Enabled;
//關於跨域限制
//bset.WebSecurity = CefState.Disabled;
browser.BrowserSettings = bset;
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
//打開網頁
//browser.Load(strMenu + htmlDidr);
//綁定JS
//browser.RegisterJsObject("callbackObj", new CallbackObjectForJs());
// browser.FrameLoadEnd += Browser_FrameLoadEnd;
this.dockpanel.Children.Add(browser);
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
{
var visitor = new CookieVisitor(all_cookies => {
var sb = new StringBuilder();
foreach (var nameValue in all_cookies)
sb.AppendLine(nameValue.Item1 + " = " + nameValue.Item2);
//MessageBox.Show("讀取到Cookie值:" + sb.ToString());
//LogHelper.Info("讀取到Cookie值:" + sb.ToString());
});
Cef.GetGlobalCookieManager().VisitAllCookies(visitor);
}
private void Browser_IsBrowserInitializedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
try
{
if ((bool)e.NewValue == true)
{
bInited = true;
}
if (browser.IsBrowserInitialized)
{
//自動播放flash
Cef.UIThreadTaskFactory.StartNew(() =>
{
string error = "";
var requestContext = browser.GetBrowser().GetHost().RequestContext;
requestContext.SetPreference("profile.default_content_setting_values.plugins", 1, out error);
});
}
}
catch(Exception ex)
{
LogHelper.Error(ex);
}
}
/// <summary>
/// 打開指定網址
/// </summary>
/// <param name="url">網址</param>
public void Open(string url)
{
//打開網頁
if (browser != null && browser.IsBrowserInitialized)
browser.Load(url);
else
browser.Address = url;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (browser != null)
{
browser.Dispose();
browser = null;
}
}
}
}
4.用cef給Chromium瀏覽器寫Cookie
有時候需要實現免登錄打開網頁,即你之前已經登錄過網站,打開該網站內其他網頁不需要再登錄。這就需要根據該網頁的驗證方式進行相應適配了,比如寫cookie,以下就是寫cookie的實現: /// <summary>
/// 自動設置token到cookie中
/// </summary>
public static void SetCookies()
{
if (!string.IsNullOrEmpty(CommCach.Token))
{
lock(lockObj)
{
try
{
//獲取portalurl中的域名地址
int starti = CommCach.PortalUrl.IndexOf("//") + 2;
int endi = CommCach.PortalUrl.IndexOf("/", starti);
string domain = CommCach.PortalUrl.Substring(starti, endi - starti);
var cookieManager = CefSharp.Cef.GetGlobalCookieManager();
cookieManager.SetCookieAsync("http://" + domain, new CefSharp.Cookie()
{
Domain = domain,
Name = "Admin-Token",
Value = HttpUtility.UrlEncode(CommCach.Token),
Expires = DateTime.MinValue
});
}
catch(Exception ex)
{
LogHelper.Error("寫Cookie失敗",ex);
}
}
}
}
以上是我測試的代碼,請根據你自己的實際要求,修改Cookie名稱和域名等。 注意SetCookieAsync的url要是http://開頭。domain不要http
5.用Cef在http請求頭中加字段
這個也是一個常見需求。以下代碼實現在http頭中增加access-token字段。
增加一個ResourceRequestHandler的自定義類:
using CefSharp;
using CefSharp.Handler;
namespace xiaoy.Control
{
public class CustomResourceRequestHandler : ResourceRequestHandler
{
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame,
IRequest request, IRequestCallback callback)
{
var headers = request.Headers;
//自動增加access-token
if (!string.IsNullOrEmpty(CommCach.Token))
{
bool bExist = false;
foreach (string k in headers.Keys)
{
if (string.Compare(k, "access-token", true) == 0)
{
bExist = true;
break;
}
}
if (bExist)
headers["access-token"] = CommCach.Token;
else
headers.Add("access-token", CommCach.Token);
}
request.Headers = headers;
return CefReturnValue.Continue;
}
}
public class CustomRequestHandler : RequestHandler
{
protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser,
IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
{
return new CustomResourceRequestHandler();
}
}
}
然后在瀏覽器對象創建后,增加handler
browser = new ChromiumWebBrowser(); browser.RequestHandler=new CustomRequestHandler();
6.用Cef打開網頁訪問本地圖片等資源
如果要打開本地網頁,cef支持loadhtml方法,但是瀏覽器權限無法打開本地資源,必須做特殊處理才可以。
官網的方案如下:https://github.com/cefsharp/CefSharp/wiki/General-Usage#initialize-and-shutdown
這是一個歪果仁在37.0老版本上實現的:https://thechriskent.com/tag/cefcustomscheme/
首先實現一下ISchemeHandlerFactory接口,自定義資源解析:
using CefSharp;
using System;
using System.IO;
using System.Reflection;
namespace xiaoy.Control
{
public class CefSharpSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
var fileName = request.Url.Replace(SchemeName+"://", "");
Assembly ass = Assembly.GetExecutingAssembly();
String resourcePath = ass.GetName().Name + "." + fileName.Replace("/", ".");
Stream resource= ass.GetManifestResourceStream(resourcePath);
if (resource!=null)
{
var fileExtension = Path.GetExtension(fileName);
return ResourceHandler.FromStream(resource, mimeType:Cef.GetMimeType(fileExtension));
}
return null;
}
}
}
然后在初始化cef的時候(上文ChromeCefHelper中)添加
//訪問本地資源
set.RegisterScheme(new CefCustomScheme
{
SchemeName = CefSharpSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CefSharpSchemeHandlerFactory()
});
//啟用配置
//bool bint= CefSharp.Cef.Initialize(set);
Cef.Initialize(set, performDependencyCheck: false, browserProcessHandler: null);
因為你定義的schemename是“custom”,所以在你本地要打開的html中,資源地址要用“custom://”開頭。
<div class="content-container">
<div class="head-line">
<img src="custom://html/error.png" alt="" width="120"/>
</div>
<div class="subheader">
{errorTitle}</div>
<div class="hr"></div>
<div class="context">
<p>
{errorContent}
</p>
</div>
</div>
然后可以直接調用 browser.LoadHtml方法顯示本地網頁了。本地網頁模板建議先讀取到內存中。
/// <summary>
/// 顯示錯誤信息
/// </summary>
/// <param name="errTitle"></param>
/// <param name="errContent"></param>
public void ShowError(string errTitle,string errContent)
{
Task.Factory.StartNew(() => {
//網頁中可替換的變量: {imagePath} {errorTitle} {errorContent}
//string imgpath = AppDomain.CurrentDomain.BaseDirectory + "html\\error.png";
//imgpath= imgpath.Replace("\\", "/");
string imgpath = CefSharpSchemeHandlerFactory.SchemeName+"://html/error.png";
//
string html = ChromeCefHelper.ErrhtmlTemplate.Replace("{imagePath}", imgpath).Replace("{errorTitle}", errTitle).Replace("{errorContent}", errContent);
//string html = string.Format(ChromeCefHelper.ErrhtmlTemplate, errTitle, errContent);
while (bInited == false)
Thread.Sleep(50);
browser.LoadHtml(html, CefSharpSchemeHandlerFactory.SchemeName+"://error.html");
});
}
7.Cef中讓瀏覽器支持F5/F12等快捷鍵
內嵌的chromium瀏覽器是默認不支持快捷鍵的,需要添加keyboardhandler支持。
using CefSharp;
using System;
using System.Windows.Forms;
namespace xiaoy.Control
{
public class CustomKeyBoardHander : IKeyboardHandler
{
public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
{
if (type == KeyType.KeyUp && Enum.IsDefined(typeof(System.Windows.Forms.Keys), windowsKeyCode))
{
var key = (Keys)windowsKeyCode;
switch (key)
{
case Keys.F12:
browser.ShowDevTools();
break;
case Keys.F5:
if (modifiers == CefEventFlags.ControlDown)
{
//MessageBox.Show("ctrl+f5");
browser.Reload(true); //強制忽略緩存
}
else
{
//MessageBox.Show("f5");
browser.Reload();
}
break;
}
}
return false;
}
public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode,
int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut)
{
return false;
}
}
}
然后在瀏覽器對象創建后增加handler
browser = new ChromiumWebBrowser(); // browser.RequestHandler=new CustomRequestHandler(); browser.KeyboardHandler = new CustomKeyBoardHander();
轉自:https://www.cnblogs.com/tuyile006/p/13852630.html

