由于项目需要,开始尝试在C#客户端+WEB的方式进行系统开发,考虑可集成内核包括IE、webkit。IE的集成最为简单,.net提供了完善的开发接口,但是考虑IE浏览器广泛的口碑问题以及糟糕的扩展能力,最终选择放弃。基于webkit内核的C#封装有很多,目前较为稳定的封装就是CefGlue,因此开始尝试基于CefGlue开发客户端程序。
集成期间遇到很多问题,大部分万能的度娘都可以给出解决办法,在此感谢诸位先行者的分享。
主要参考文章:
.NET多种WebKit内核/Blink内核浏览器初步测评报告【系列】
浏览器的集成比较简单,网上也有很多文章详细的讲述了集成的方法,同时也有很多对于webkit基本架构原理的介绍,这里不再重复,主要需要注意的是CEF和webkit内核的版本匹配,这点可以参考《基于.net开发chrome核心浏览器》系列文章第二篇。
JS和C#交互的文章网上也有很多《 .NET多种WebKit内核/Blink内核浏览器初步测评报告》系列文章中也探讨尝试了很多调用方法,大致总结一下从调用方向来看可以分为JavaScript调用C#和C#调用JavaScript两种(这句是废话,但是必须得说)。
一、JavaScript调用C#
JavaScript调用C#,可以为页面开放本地功能扩展,也是大部分人比较关注的内容,基本有两种方式。
1、使用CefRuntime.RegisterExtension函数注册并开放接口函数
继承Xilium.CefGlue.CefRenderProcessHandler类并重写OnWebKitInitialized方法,在方法当中可以通过调用CefRuntime.RegisterExtension(string extensionName, string javascriptCode, CefV8Handler handler)方法注册全局JS接口,这种方法的实现方式可以参考《第3篇:Xilium CefGlue 关于 CLR Object 与 JS 交互类库封装报告:官方原生方法分析 》中的讲述,只需要注意“扩展和窗体绑定类似,除了在每个框架的上下文中加载和加载后不能修改。当扩展加载后DOM不存在和在扩展加载期间试图访问DOM将会导致崩溃《CEF3开发者系列之JS与C++交互之二》”。
2、通过CefV8Context的全局变量注册函数
继承Xilium.CefGlue.CefRenderProcessHandler类并重写OnContextCreated方法,在方法当中通过执行
var global = context.GetGlobal(); var extent = CefV8Value.CreateObject(null); global.SetValue("V8", extent, CefV8PropertyAttribute.None);
通过CefV8Value CreateFunction(string name, CefV8Handler handler)方法创建JS函数发布接口。
二、C#调用JavaScript
1、通过CefFrame.ExecuteJavaScript(string code, string url, int line)方法执行JS
-
///
<summary>
-
/// Execute a string of JavaScript code in this frame. The |script_url|
-
/// parameter is the URL where the script in question can be found, if any.
-
/// The renderer may request this URL to show the developer the source of the
-
/// error. The |start_line| parameter is the base line number to use for error
-
/// reporting.
-
///
</summary>
-
public void ExecuteJavaScript(string code, string url, int line)
此方法可以执行指定frame上的一段js脚本,使用这种方法的优点调用比较直观,但是缺点是只能调用命名函数,对于匿名方法回调无能为力。
2、通过CefV8Value.ExecuteFunction或CefV8Value.ExecuteFunctionWithContext执行
-
///
<summary>
-
/// Execute the function using the current V8 context. This method should only
-
/// be called from within the scope of a CefV8Handler or CefV8Accessor
-
/// callback, or in combination with calling Enter() and Exit() on a stored
-
/// CefV8Context reference. |object| is the receiver ('this' object) of the
-
/// function. If |object| is empty the current context's global object will be
-
/// used. |arguments| is the list of arguments that will be passed to the
-
/// function. Returns the function return value on success. Returns NULL if
-
/// this method is called incorrectly or an exception is thrown.
-
///
</summary>
-
public CefV8Value ExecuteFunction(CefV8Value obj, CefV8Value[] arguments)
-
///
<summary>
-
/// Execute the function using the specified V8 context. |object| is the
-
/// receiver ('this' object) of the function. If |object| is empty the
-
/// specified context's global object will be used. |arguments| is the list of
-
/// arguments that will be passed to the function. Returns the function return
-
/// value on success. Returns NULL if this method is called incorrectly or an
-
/// exception is thrown.
-
///
</summary>
-
public CefV8Value ExecuteFunctionWithContext(CefV8Context context, CefV8Value obj, CefV8Value[] arguments)
这两种方法适合作为函数回调的执行方法,但是调用时需要多注意线程的切换。可以利用MessageRouter在Browser和Render进程之间传递消息记性调用,具体请参看后续文章