准备工作:
下载CefSharp,过程略,网上教程一堆,NUGET分分钟下载。
注意事项:
官方的CefSharp只能是x86或x64,不能编译AnyCPU,如果选择AnyCPU会报错。
CefSharp在程序结束时必须调用Shutdown()方法来释放资源,否则会吃内存。
目前我使用的是63.0,直接NUGET下载的,版本不同变化会很大。
本文目的是将HTML、JS、图片等资源集成到EXE中直接访问,而不用将页面暴露出来。
本文有cefSharp禁止右键方法,不需要可以去掉。
cefSharp的ChromiumBrowser并没有默认支持热键,所以不需要禁用,如果需要可以自己写事件。
所有资源(html、js、png、jpg等)必须放到窗口的资源中,注意改名。
开始撸
html文件,这个我就不发所有资源了,只发一小段
<body> <div class="wrapper clear"> <!--正经玩意start--> <div class="topbar"> <div class="toptitle" id="netbarName">欢迎光临</div> <div id="curTime" class="toptime">2018年05月12日 15:41:00</div> </div> <div class="hugeAD"><img src="images/Ad.png"></div> <div class="loginbar"> <div class="logintitle">会员登录</div> <div class="logintitle"><img src="images/login.png"/></div> <div class="loginlabel">证件号</div> <div class="form-group"> <input id="cardNumber" type="text" class="inp" placeholder="输入证件号"> <input type="button" id="loginSubmit" class="submit" value="登录" onclick="cl()"></div> </div> </div> <script> function chgBtn(enb,text) { var sb = $('#loginSubmit'); if (enb) sb.css('background-color', '#b6b1a6'); else sb.css('background-color', '#fec43a'); if (enb) sb.attr("disabled", true); else sb.removeAttr("disabled"); if (text != null) sb.val(text); } //这个是调用winform中的方法 function cl() { if ($('#loginSubmit').attr("disabled") == "disabled") return; frm.showMsg($('#cardNumber').val()); } function setTitle(netbarName,curTime) { $('#netbarName').text("欢迎光临 " + netbarName); $('#curTime').text(curTime); } </script> </body>
2.实现MenuHandler,为了禁止右键显示菜单
class BrowserMenuHandler : IContextMenuHandler { void IContextMenuHandler.OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) { return; } bool IContextMenuHandler.OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) { return true; } void IContextMenuHandler.OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame) { return; } bool IContextMenuHandler.RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback) { return true; } }
3.自定义Scheme接口,实现自协议类型,并访问C# Winform的内嵌资源
public class CustomSchemeHandler : IResourceHandler { //内存流 private MemoryStream ms; //mimeType,后面输出的时候要用 private string mimeType; //资源列表 Dictionary<string, byte[]> WebRes; public CustomSchemeHandler() { //将所有资源对应到字典中,不同类型的文件在资源中是不同的类型 //例如图片就是bigmap,html文件就是string,需要将其全部转换成byte[] Encoding ec = Encoding.UTF8; WebRes = new Dictionary<string, byte[]>(); WebRes.Add("/index.html",ec.GetBytes( Properties.Resources.index_html)); WebRes.Add("/images/Edit.png", imgToByte(Properties.Resources.Edit_png)); WebRes.Add("/images/BgMain.png", imgToByte(Properties.Resources.BgMain)); WebRes.Add("/js/index.js", ec.GetBytes(Properties.Resources.index_js)); WebRes.Add("/js/jquery-2.1.4.min.js", ec.GetBytes(Properties.Resources.jquery_2_1_4_min_js)); WebRes.Add("/js/jquery-3.3.1.min.js", ec.GetBytes(Properties.Resources.jquery_3_3_1_min_js)); WebRes.Add("/login.html", ec.GetBytes(Properties.Resources.login_html)); WebRes.Add("/images/login.png", imgToByte(Properties.Resources.login_png)); WebRes.Add("/images/Ad.png", imgToByte(Properties.Resources.Ad_png)); } private byte[] imgToByte(Bitmap img) { ImageConverter converter = new ImageConverter(); return (byte[])converter.ConvertTo(img, typeof(byte[])); } void IResourceHandler.Cancel() { } bool IResourceHandler.CanGetCookie(Cookie cookie) { return false; } bool IResourceHandler.CanSetCookie(Cookie cookie) { return false; } #region 包装responseheader void IResourceHandler.GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl) { redirectUrl = null; response.MimeType = mimeType; if(ms !=null) { responseLength = ms.Length; response.StatusCode = (int)System.Net.HttpStatusCode.OK; response.StatusText = "OK"; }else { responseLength = 0; response.StatusCode = (int)System.Net.HttpStatusCode.NotFound; response.StatusText = "NotFound"; } } #endregion #region 收到请求处理 bool IResourceHandler.ProcessRequest(IRequest request, ICallback callback) { var uri = new Uri(request.Url); var resName = uri.AbsolutePath; byte[] res; if(WebRes.TryGetValue(resName,out res)) { Task.Run(() => { using(callback) { ms = new MemoryStream(res); string ext = Path.GetExtension(resName); mimeType = ResourceHandler.GetMimeType(ext); callback.Continue(); } }); return true; } else { mimeType = ResourceHandler.GetMimeType(".html"); ms = null; callback.Continue(); } return false; } #endregion #region 包装Body bool IResourceHandler.ReadResponse(Stream dataOut, out int bytesRead, ICallback callback) { callback.Dispose(); if(ms==null) { bytesRead = 0; return false; } var buffer = new byte[dataOut.Length]; bytesRead = ms.Read(buffer, 0, buffer.Length); dataOut.Write(buffer, 0, buffer.Length); return bytesRead > 0; } #endregion #region IDisposable Support void IDisposable.Dispose() { if(ms !=null) { ms.Close(); ms.Dispose(); ms = null; mimeType = null; } } #endregion }
4.创建自定义Scheme工厂类
class CustSchemeFactory : ISchemeHandlerFactory { //协议名称,注意http和https是无效的会直接访问,这里我写的netbar,在访问时就是netbar://xxx/xxx.js这样的地址 public const string SchemeName = "netbar"; IResourceHandler ISchemeHandlerFactory.Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) { if(schemeName==SchemeName) { return new CustomSchemeHandler(); }else { var mimeType = ResourceHandler.GetMimeType(".html"); return ResourceHandler.FromFilePath("abc.html", mimeType);//这里是404页面,我这里随便写一个并不存在的文件。 } } }
5.初始化并设置cef参数,展示页面
private void FrmLogin_Shown(object sender, EventArgs e) { //cefSharp参数 CefSettings cefset= new CefSettings() { Locale = "zh-CN", AcceptLanguageList = "zh,zh-cn", CachePath = Application.StartupPath + @"\cache", IgnoreCertificateErrors = true }; //这一步是为了后面winform与JS交互 CefSharpSettings.LegacyJavascriptBindingEnabled = true; //这一步是注册自定义Scheme,让浏览器可以认出 netbar://abc/def.html这样的地址 cefset.RegisterScheme(new CefCustomScheme { SchemeName = CustSchemeFactory.SchemeName, SchemeHandlerFactory = new CustSchemeFactory() }); //初始化,这一步必须在任意一个new ChromiumWebBrowser之前做 Cef.Initialize(cefset); //这里的地址netbar://abc/login.html,netbar是我们自己定义的,abc是域(domain)这个随便填, //后面的文件名必须跟上一步中的字典中一样,否则404,而由于我们并没有设置404页面的地址,所以会变空白页 wb = new ChromiumWebBrowser("netbar://abc/login.html"); wb.Dock = DockStyle.Fill; wb.Padding = new Padding(0); wb.Margin = new Padding(0); //这一步很重要,这是将当前窗口类注册给页面 //在页面中用js就可以调用当前窗口中的公开方法 //在JS中可以使用frm.showMsg()方法就可以调用本窗口中的相同方法 //注意:必须使用首字母小写的方法名例如showMsg不能写成ShowMsg(),否则不认 // CefSharpSettings.LegacyJavascriptBindingEnabled = true;必须有,否则无法绑定 // JS调用本窗口的方法是独立线程,如果要操作窗体上的控件要注意使用Invoke wb.RegisterJsObject("frm", this); wb.FrameLoadEnd += Wb_FrameLoadEnd; //绑定MenuHandler,这一步是为了禁止右键,这个MenuHandler没有自动提醒,只能手动打上去 wb.MenuHandler = new BrowserMenuHandler(); Controls.Add(wb); } private void Wb_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) { //载入网页完成 if (e.Frame.IsMain) { //这一步判断是不是主frame,如果是的话就执行页面上的一个脚本chgBtn(); e .Browser .MainFrame .ExecuteJavaScriptAsync( "chgBtn(true,'登录')"); } } public void showMsg(string msg) { MessageBox.Show(msg); }
此时运行程序就可以访问EXE中内嵌的网页资源了,教程写的比较简单,有些东西需要自己填上去。
还没有评论,来说两句吧...