当前位置:   article > 正文

【WinForm】用ChromiumWeb内核开发一个自用的浏览器_自己开发chrome内核浏览器

自己开发chrome内核浏览器

需要一个自用的浏览器,最好是用ChromiumWeb内核快速开发,下面使用Visual Studio 开发工具新建一个桌面程序WinForm项目,实现一个基本上网浏览功能使用即可

  1. 选择项目,鼠标右键选择打开Nuget程序包管理器,插件上找CefSharp 安装好,如下图所示,由于此插件只支持x64或x86的处理器,若编译运行不了就设置编译为x64或x86即可
    在这里插入图片描述

  2. 把窗口布局弄成大致如下图所示
    在这里插入图片描述

窗口有包括返回、刷新、关闭、(搜索)访问、打印、开发者工具按钮等,还有输入框
用的两大组件: TabControl,ToolStrip,里面在包含一些子组件Label,TextBox,Button…

  1. 开始写代码,处理初始化,在组件TabControl中添加浏览器组件,代码如下
public partial class Form2 : Form
{
    ChromiumWebBrowser mChromium;

    public Form2()
    {
        InitializeComponent();
        InitializeChromium();//调用初始化浏览器方法
    }

    private void InitializeChromium()
    {
        var tempPath = Path.Combine(System.Environment.GetEnvironmentVariable("TMP"), "mybrowser");//用户临时目录
        if (Directory.Exists(tempPath)!=true)
        {
            Directory.CreateDirectory(tempPath);
        }
        var settings = new CefSettings();
        settings.UserDataPath = Path.Combine(tempPath, "userData");
        settings.CachePath = Path.Combine(tempPath, "cache");//缓存路径
        settings.LogFile = Path.Combine(tempPath, "logFile");//日志文件
        //settings.ResourcesDirPath = Path.Combine(tempPath, "resDir");
        //settings.No = true;
        //settings.Locale = "zh-CN";
        //settings.LocalesDirPath = Path.Combine(tempPath, "localesDir");
        settings.AcceptLanguageList = "zh-CN,zh;q=0.8";//引擎语言
        settings.PersistSessionCookies = true;
        settings.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36";
        CefSharp.Cef.Initialize(settings, true);

        if (CefSharpSettings.ShutdownOnExit)
        {
            Application.ApplicationExit += OnApplicationExit;//设置退出时调用的方法事件
        }

        tabControl1.TabPages[0].Text = "主页";
        tabControl1.TabPages[1].Text = "loading...";

        var url = toolStripTextBox1.Text;//使用默认地址
        OpenNewBrowser(tabControl1.SelectedIndex + 1, url);//打开浏览器方法

    }
    
    private void OnApplicationExit(object sender, EventArgs e)
    {
        Cef.Shutdown();//释放资源
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

4.打开浏览器方法,实现细节很多,代码如下

public partial class Form2 : Form
{
    
    private void OpenNewBrowser(int tabIndex, string url)
    {
        var browser = new ChromiumWebBrowser(url);
        //var browser = new ChromiumWebBrowser();
        //标题改变事件
        browser.TitleChanged += new EventHandler<TitleChangedEventArgs>(delegate (object sender, TitleChangedEventArgs args) {
            this.Invoke(new Action(delegate ()
            {
                if (tabIndex < tabControl1.TabPages.Count)
                {
                    tabControl1.TabPages[tabIndex].Text = args.Title;
                }
            }));
        });
		//加载错误处理事件
        browser.LoadError += new EventHandler<LoadErrorEventArgs>(delegate (object sender, LoadErrorEventArgs args) {
            var errorTemplate = string.Format(HtmlTemplate.ErrorPage3, args.ErrorText, args.ErrorCode.ToString(), args.FailedUrl);
            browser.LoadHtml(errorTemplate);
        });
		//状态改变事件 例如 用户鼠标指向的链接
        browser.StatusMessage += new EventHandler<StatusMessageEventArgs>((object sender, StatusMessageEventArgs args) => {
            this.Invoke(new Action(() => {
                if (string.IsNullOrEmpty(args.Value))
                {
                    label1.Text = "";
                    return;
                }
                label1.Text = string.Format("预点击链接:{0}", args.Value);
            }));
        });
		//浏览器地址跳转 改变事件
        browser.AddressChanged += new EventHandler<AddressChangedEventArgs>((object sender, AddressChangedEventArgs args) => {
            this.Invoke(new Action(() => {
                if (toolStripTextBox1.Focused != false) return;
                toolStripTextBox1.Text = args.Address;
            }));
        });
		//对打开新页面时做处理
        browser.LifeSpanHandler = new CefLifeSpanHandler((IWindowInfo windowInfo, String targetUrl) => {
            this.Invoke(new Action(() => {
                tabControl1.TabPages.Add(targetUrl);
                OpenNewBrowser(tabControl1.TabPages.Count - 1, targetUrl);
            }));
        });
		//下载事件
        browser.DownloadHandler = new CefDownloadHandler("_007",(String targetUrl, String id) => {
            var res = MessageBox.Show(String.Format("下载完成,是否打开文件位置?\n{0}", targetUrl), "系统提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
            if (res == DialogResult.OK)
            {
                openExplorer(targetUrl);//打开文件位置
            }
            return true;
        });
		//鼠标右键菜单
        browser.MenuHandler = new CefMenuHandler();

        browser.Dock = System.Windows.Forms.DockStyle.Fill;
		//添加浏览器组件
        tabControl1.TabPages[tabIndex].Controls.Add(browser);

        if (tabControl1.SelectedIndex != tabIndex)
        {
            tabControl1.SelectedIndex = tabIndex;
        }

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  1. 浏览器的一些处理事件有调用了自定义的类,分别是CefLifeSpanHandlerCefDownloadHandlerCefMenuHandler,贴上代码如下
  • 处理打开新页面(_blank)
public class CefLifeSpanHandler : CefSharp.ILifeSpanHandler
{
    Action<IWindowInfo, String> onNewWindow;

    public CefLifeSpanHandler(Action<IWindowInfo, String> OnNewWindow)
    {
        this.onNewWindow = OnNewWindow;
    }

    public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
    {
        if (browser.IsDisposed || browser.IsPopup)
        {
            return false;
        }

        return true;
    }

    public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
    {
        //throw new NotImplementedException();
    }

    public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
    {
        //throw new NotImplementedException();
    }

    public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
    {
        if (onNewWindow != null)
        {
            onNewWindow(windowInfo, targetUrl);//调用传入的方法
        }

        newBrowser = null;
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 处理下载文件的
public class CefDownloadHandler : CefSharp.IDownloadHandler
{
    Func<string, string, bool> calDownload;
    String id;

    public CefDownloadHandler(String id, Func<string, string, bool> calDownload)
    {
        this.calDownload = calDownload;
        this.id = id;
    }

    public bool CanDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, string url, string requestMethod)
    {
        return true;
    }

    public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
    {
        callback.Continue(downloadItem.Url, true);
    }

    public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
    {
        if (downloadItem.IsComplete != true) return;
        //下载成功时调用
        calDownload(downloadItem.FullPath, id);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 处理弹出右键菜单的
//封装命令集的类 如枚举
class MenuCommand
{
    public static CefMenuCommand CopySelectText = (CefMenuCommand) 1000011;
    public static CefMenuCommand DownloadSave = (CefMenuCommand) 1000012;
    public static CefMenuCommand CopyLink = (CefMenuCommand) 1000013;
}

public class CefMenuHandler : CefSharp.IContextMenuHandler
{
    public void OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
    {
        if (model.Count>0)
        {
            model.AddSeparator();
        }
        if (!String.IsNullOrEmpty(parameters.SourceUrl))
        {
            if (parameters.MediaType != ContextMenuMediaType.None)
            {
                model.AddItem(MenuCommand.DownloadSave, "save file");//保存文件
            }
        }
        else if (!String.IsNullOrEmpty(parameters.LinkUrl))
        {
            var reg = new Regex("\\.\\w+$");
            if (reg.IsMatch(parameters.LinkUrl))
            {
                model.AddItem(MenuCommand.DownloadSave, "save file");//保存文件
            } else
            {
                model.AddItem(MenuCommand.CopyLink, "copy link");//复制链接
            }
            return;
        }
        
    }

    public bool OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
    {
        if (commandId == MenuCommand.CopyLink)
        {
            Clipboard.SetText(parameters.SelectionText);
        }
        else if (commandId == MenuCommand.DownloadSave)
        {
            var cbrowser = chromiumWebBrowser as ChromiumWebBrowser;
            if (!string.IsNullOrEmpty(parameters.SourceUrl))
            {
                cbrowser.StartDownload(parameters.SourceUrl);
                return true;
            }
            else if (!string.IsNullOrEmpty(parameters.LinkUrl))
            {
                cbrowser.StartDownload(parameters.LinkUrl);
                return true;
            }
        }
        return false;
    }

    public void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame)
    {
        var cbrowser = chromiumWebBrowser as ChromiumWebBrowser;
        cbrowser.Invoke(new Action(() =>
        {
            cbrowser.ContextMenu = null;
        }));
    }

    public bool RunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
    {
        return false; //可弹出
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  1. 最后,补充完善一下用户的交互逻辑,如点击按钮有返回、刷新、关闭、访问等,代码如下
public partial class Form2 : Form
{
    ChromiumWebBrowser mChromium;

    public Form2()
    {
        InitializeComponent();
        InitializeChromium();
    }

    private void InitializeChromium()
    {
		//...
    }

    private void OpenNewBrowser(int tabIndex, string url)
    {
		//...
    }

    /// <summary>
    /// 打开文件位置
    /// </summary>
    /// <param name="filePath">文件路径</param>
    private void openExplorer(string filePath)
    {
        filePath = filePath.Replace("/", "\\");//此处的路径都要求是右斜杠的’\’才能定位打开
        System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("Explorer.exe");
        psi.Arguments = "/e,/select," + filePath;
        System.Diagnostics.Process.Start(psi);
    }

    private void Form2_Load(object sender, EventArgs e)
    {

    }

    private void OnApplicationExit(object sender, EventArgs e)
    {
		//...
    }

    private void Form2_FormClosing(object sender, FormClosingEventArgs e)
    {
        try
        {
            for(int i=1; i< tabControl1.Controls.Count; i++)
            {
                var control = tabControl1.Controls[i];
                if (control.Controls.Count <= 0) continue;
                CloseBrowser(control.Controls[0] as ChromiumWebBrowser);
                control.Controls.Clear();
            }
            //KillBrowserSubprocess();//杀死进程
            //if (isShutdown) CefSharp.Cef.Shutdown();//释放资源
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString(), "清理CefSharp.BrowserSubprocess异常");
        }
    }

    private void CloseBrowser(ChromiumWebBrowser cefBrowser)
    {
        cefBrowser.JavascriptObjectRepository.UnRegisterAll();//解绑对象 高版本才有

        cefBrowser.CloseDevTools();//关闭调试
        cefBrowser.GetBrowser().CloseBrowser(true);//关闭浏览器
        cefBrowser.Dispose();
    }
	//刷新按钮点击
    private void toolStripButton2_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.Refresh();
    }
	//获取当前选项卡下的浏览窗口
    private ChromiumWebBrowser GetCurrorBrowser()
    {
        if (tabControl1.SelectedIndex < 1) return null;
        return tabControl1.SelectedTab.Controls[0] as ChromiumWebBrowser;
    }
	//返回按钮点击
    private void toolStripButton1_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.Back();
    }
	//关闭当前选项卡
    private void toolStripButton3_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;
        //TODO: 有Bug,当它删掉两个就会挂掉 错误未知  需要优化
        CloseBrowser(browser);

         if (tabControl1.SelectedIndex > 0)
         {
             tabControl1.SelectedIndex--;
             tabControl1.TabPages.RemoveAt(tabControl1.SelectedIndex + 1);
         }
    }
	//地址输入框 的 访问按钮点击
    private void toolStripButton4_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        var text = toolStripTextBox1.Text.Trim();
        if (string.IsNullOrEmpty(text)) return;

        if (!text.StartsWith("http"))
        {
            if (!text.StartsWith("//"))
            {
                text = "//" + text;
            }
            if (!text.StartsWith(":"))
            {
                text = ":" + text;
            }
        }

        if (toolStripTextBox1.Text.Contains(text))
        {
            toolStripTextBox1.Text = text;
        }

        browser.LoadUrlAsync(text);
    }

	//主页 百度一下 按钮点击
    private void button1_Click(object sender, EventArgs e)
    {
        var text = textBox1.Text.Trim();
        if (string.IsNullOrEmpty(text)) return;

        var url = "https://www.baidu.com/s?ie=utf-8&wd=" + HttpUtility.UrlEncode(text);
        tabControl1.TabPages.Add(url);
        OpenNewBrowser(tabControl1.TabPages.Count-1, url);
    }
	//打印按钮点击
    private void toolStripButton5_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.Print();
    }
	//打开开发者工具 控制台 调试
    private void toolStripButton6_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.ShowDevTools();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  1. 忘了还有HtmlTemplate类,是一个基本网页模板代码,还是贴出来参考,代码如下
public class HtmlTemplate
{
    internal static readonly string ErrorPage3 = "<!doctype html>" +
        "<html>" +
        "<head>" +
        "<meta charset=\"utf-8\" />" +
        "<title>Error Page</title>" +
        "</head>" +
        "<body style=\"padding: 20px; background-color: #0000ff; color: #ffffff;\">" +
        "<h1>:(</h1>" +
        "<h3>{0}</h3>" +
        "<hr/>" +
        "<p>错误信息:{1}</p>" +
        "<p>错误地址:<a style=\"opacity: 0.5; color: chartreuse; text-decoration: none;\" href=\"javascript:return false;\">{2}</a></p>" +
        "</body>" +
        "</html>";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

8.就记录到这了,收工!

该浏览器只实现后,经过测试如下:

  1. 支持HTML5标准
  2. 可下载文件
  3. 可播放视频MP4 在线播放其它视频格式暂时不支持
  4. 可自定义右键菜单
  5. 已禁止播放flash插件
  6. 其它…脚本与浏览器通信…未测

在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号