• Cef框架的使用:内嵌Chromium内核浏览器


    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

  • 相关阅读:
    windows 资源管理器 运行非常缓慢或者无反应的解决办法
    RPC工作原理
    如何解决访问某些网站会跳出对话框标题为:address Book Viewer ,提示:"无法连接制定的目录服务.服务也许暂时不可用,或服务器名称不正确。"
    运行程序的时候出错MFC42D.dll找不到。
    谷歌金山词霸合作版升级后出现CBSText.dll故障
    Network drives trying to connect when offline
    解决任务挂起或冻结在Windows XP
    打开word或者office程序报错:Microsoft Visual C++ Runtime Library. Runtime Error!
    恢复officescan控制台密码
    excel中同时冻结窗格--冻结行列标题
  • 原文地址:https://www.cnblogs.com/javalinux/p/14283175.html
Copyright © 2020-2023  润新知