C# 使用cefSharp访问EXE内嵌资源,禁止右键

准备工作:

下载CefSharp,过程略,网上教程一堆,NUGET分分钟下载。

注意事项:

  1. 官方的CefSharp只能是x86或x64,不能编译AnyCPU,如果选择AnyCPU会报错。

  2. CefSharp在程序结束时必须调用Shutdown()方法来释放资源,否则会吃内存。

  3. 目前我使用的是63.0,直接NUGET下载的,版本不同变化会很大。

  4. 本文目的是将HTML、JS、图片等资源集成到EXE中直接访问,而不用将页面暴露出来。

  5. 本文有cefSharp禁止右键方法,不需要可以去掉。

  6. cefSharp的ChromiumBrowser并没有默认支持热键,所以不需要禁用,如果需要可以自己写事件。

  7. 所有资源(html、js、png、jpg等)必须放到窗口的资源中,注意改名。

开始撸

  1. 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中内嵌的网页资源了,教程写的比较简单,有些东西需要自己填上去。

标签:
版权所有原创文章,转载请保留或注明出处:https://www.261k.com/post/20180512081138.html