前言:如果你想在网站指定地方注入自己的JS,然后通过JS和C#函数互调,那么这篇文章对你很有用
优势:相比WebKit.NET,CefSharp的谷歌浏览器内核比较新,不会出现H5特性不支持导致的网站无法载入空白的情况,非常不建议大家去使用WebKit.NET,因为内核实在太低。
思路: 使用ChromiumWebBrowser载入浏览器,在FrameLoadEnd事件之后注入自定义的JS,通过TitleChanged事件接受前端通知。
环境: 最低要求 .NET Framework 4.5.2、VS2013
部署: 去下载包cef.redist.x64、cef.redist.x86、cefsharp.common、cefsharp.winforms(我采用的是winforms开发)
https://packages.nuget.org/packages/cef.redist.x64/
手册:https://www.cnblogs.com/yanpeng19940119/archive/2019/10/24/11722377.html
初始化浏览器:
wb = new ChromiumWebBrowser("http://*****");
浏览器加载完成注入ajax函数:
wb.FrameLoadEnd += wb_FrameLoadEnd;
void wb_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) { //注册ajax方法 StringBuilder sb = new StringBuilder(); sb.AppendLine("function _ajax(type,url,params,contentType,fun) {"); sb.AppendLine(@"$.ajax({ url:url, type: type, async: true, data: params, dataType: 'json', contentType: contentType, success: function(data) { fun(JSON.stringify(data)); }, error: function(xhr, textStatus) { fun(xhr.responseText); } })"); sb.AppendLine("};"); var task01 = wb.GetBrowser().GetFrame(wb.GetBrowser().GetFrameNames()[0]).EvaluateScriptAsync(sb.ToString()); }
TitleChanged事件
这里我需要一点点技巧,我们为浏览器添加一个TitleChanged事件,当ajax请求完毕后我们更改wb.Title就会触发此事件
wb.TitleChanged += wb_TitleChanged;
void wb_TitleChanged(object sender, TitleChangedEventArgs e) { try { string title = e.Title; if (!string.IsNullOrEmpty(title)) { #region 获取到任务 if (title == "getTasks") { } #endregion #region 获取联系人 if (title=="contacts") { } #endregion } } catch (Exception ex) { Print("✖ 系统异常:"+ex.Message); Lib.Log(ex.ToString()); } }
调用ajax并将返回数据提交给C#后端
private void getTask() { string key = "getTasks"; string data = "{}"; string js = "_ajax('post','https://***','" + data + "','application/json',function(res){ jsEvent.setData('" + key + "',res,''); setTimeout(function(){document.title='" + key + "'},5000); });"; wb.GetBrowser().GetFrame(wb.GetBrowser().GetFrameNames()[0]).EvaluateScriptAsync(js); Print("→ 获取任务条数..."); }
代码中有一个jsEvent对方,这个对象其实是我们C#后端的CefAsyncJS对象,在前端注入进去后取名jsEvent。
前端jsEvent对象注入
这个对象的注入非常关键,因为有了它我们才能使用前端JS调用后端C#函数,我现在使用它来存/取ajax返回的内容,而这些内容都将以*.txt的形式保存在本地。
首先我们要实现CefAsyncJS对象
public class CefAsyncJS { /// <summary> /// 将存储的数据读出来 /// </summary> public string getData(string key,string custId) { string path = System.AppDomain.CurrentDomain.BaseDirectory + "data/"+key; string fileName = key + (string.IsNullOrEmpty(custId) ? "" : ("-" + custId)) + ".txt"; string fullName = path + "/" + fileName; while (!System.IO.File.Exists(fullName)) { System.Threading.Thread.Sleep(500); } return Lib.readFile(fullName);//读取文件 } /// <summary> /// 将前台传递的数据存起来 /// </summary> public void setData(string key, string value, string custId) { string path = "data/" + key; string fileName = key + (string.IsNullOrEmpty(custId) ? "" : ("-" + custId)) + ".txt"; Lib.Log(value, path, fileName,true);//保存文件 } }
现在我们在前端注入
//注册js事件 CefSharpSettings.LegacyJavascriptBindingEnabled = true; CefSharpSettings.WcfEnabled = true; wb.JavascriptObjectRepository.Register("jsEvent", jsEvent, isAsync: true, options: new CefSharp.BindingOptions() { CamelCaseJavascriptNames = false }); i++; StringBuilder sb = new StringBuilder(); sb.AppendLine(@"CefSharp.BindObjectAsync('jsEvent').then(function(result) { alert('✔函数 jsEvent 注册完成'); });"); wb.GetBrowser().GetFrame(wb.GetBrowser().GetFrameNames()[0]).EvaluateScriptAsync(sb.ToString());
总结:首先在网站添加自己的JS方法,然后再把C#对象注册到JS中,最后通过网站Title的更改通知C#读取内容。
如有技术问题,欢迎联系我邮箱咨询:norton.yang@foxmail.com