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

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

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

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

把窗口布局弄成大致如下图所示

窗口有包括返回、刷新、关闭、(搜索)访问、打印、开发者工具按钮等,还有输入框

用的两大组件: TabControl,ToolStrip,里面在包含一些子组件Label,TextBox,Button…

开始写代码,处理初始化,在组件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();//释放资源

}

}

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(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(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((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((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;

}

}

}

浏览器的一些处理事件有调用了自定义的类,分别是CefLifeSpanHandler,CefDownloadHandler,CefMenuHandler,贴上代码如下

处理打开新页面(_blank)

public class CefLifeSpanHandler : CefSharp.ILifeSpanHandler

{

Action onNewWindow;

public CefLifeSpanHandler(Action 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;

}

}

处理下载文件的

public class CefDownloadHandler : CefSharp.IDownloadHandler

{

Func calDownload;

String id;

public CefDownloadHandler(String id, Func 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);

}

}

处理弹出右键菜单的

//封装命令集的类 如枚举

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; //可弹出

}

}

最后,补充完善一下用户的交互逻辑,如点击按钮有返回、刷新、关闭、访问等,代码如下

public partial class Form2 : Form

{

ChromiumWebBrowser mChromium;

public Form2()

{

InitializeComponent();

InitializeChromium();

}

private void InitializeChromium()

{

//...

}

private void OpenNewBrowser(int tabIndex, string url)

{

//...

}

///

/// 打开文件位置

///

/// 文件路径

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();

}

}

忘了还有HtmlTemplate类,是一个基本网页模板代码,还是贴出来参考,代码如下

public class HtmlTemplate

{

internal static readonly string ErrorPage3 = "" +

"" +

"" +

"" +

"Error Page" +

"" +

"" +

"

:(

" +

"

{0}

" +

"


" +

"

错误信息:{1}

" +

"

错误地址:{2}

" +

"" +

"";

}

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

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

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

养生小贴士

前端10大开源拖拽排序库汇总, 让搭建,更简单
如何卸载该应用程序
💡 小知识

如何卸载该应用程序

📅 07-04 👍 113
【原创】【塞尔达传说 旷野之息】全驿站位置及贩售一览攻略