基于 wke 的浏览器:如何实现 js 和 c++ 的互相调用
2017-04-21 12:02
651 查看
一、引言
最近,老大给了我一个学习研讨任务,也就是如何让 js 和 C++ 进行互调使用。比如我可以在网页中,使用 js 代码调用 c++ 函数,也可以在 c++ 函数中调用 js 对于界面进行控制。这是为后期的软件接入 Html5 做技术调研。
那么如何实现呢?
这里需要感谢 Redrain 的博客给Webkit内核的浏览器控件增加互交功能,通过参考他的博客,加之我自己的探索,一步一坑终于完成了这个 demo。
如果想要直接看大神的博客可以点击上面那个链接,如果你是个跟我一样在之间一无所知的小白,那就可以看看我作为小白的一步一步的探索吧,应该会比上面大神的博客详细一些。
二、基于 SOUI 的浏览器控件
我这个浏览器 demo 是基于 SOUI 界面库,而 Redrain 的 demo 是基于 duilib 的。其中 SOUI 界面库是我工作中接触到的第一个开源的界面库,功能非常强大,感兴趣的同学可以点击这里 UI神奇-SOUI。其实用的什么界面库在这里真的没有什么大的影响,因为都是同样封装了 wke 的浏览器内核来调用而已。那么 wke 是什么呢? wke 是 BlzFans (国内大神)对于 webkit 浏览器内核的一个小巧而又强大的封装库,可以实现浏览器内核的相关功能。
那么,怎么开始呢?我们就假设你已经有了一个可以访问网页的浏览器 demo 了,并且使用的是 wke 内核。接下来我们就一步一步的实现 js 和 c++ 之间的交互吧。
三、c++ 调用 js
首先,我们来实现稍微简单点的,c++ 调用 js。如何实现呢?其实我们仔细翻看 wke.h 文件,就可以看到其实 blzFans 已经封装好了。我们打开 wke.h 文件,可以看到以下的两行代码:
virtual jsValue runJS(const utf8* script) = 0; virtual jsValue runJS(const wchar_t* script) = 0;
通过函数名称,我们就可以推测这是调用 js 的方法,那么我们只需要在封装浏览器控件的地方,封装一个函数方法,调用
runJS()方法即可。
这里我使用的是 SOUI 界面库,在 demo 里面已经封装好了一个基本成型的浏览器控件,这里直接在
SWkeWebkit类中添加
RunJS()方法即可:
SStringW SWkeWebkit::RunJS(SStringW strValue) { if (m_pWebView == NULL) return SStringW(L"wke 对象为空"); jsValue jsRet = m_pWebView->runJS(strValue); return jsToStringW(m_pWebView->globalExec(), jsRet); }
这里需要解释一下,
SStringW是 SOUI 界面库封装的字符串类,我们将 wke 对象设置为
SWkeWebkit类的私有变量进行存储,这里由
m_pWebView就可以来调用
runJS()方法调用 js 代码了。
值得一说的是最后的 return 句,这里是参考 Redrain 的代码写的,刚开始也不知道是什么含义,经过多次测试后发现,这一句可以用来返回可能需要返回的 js 代码结果,比如寻找一个元素后返回某些属性值什么的。
比如说,我在 demo 里面写了一个 index.html ,其中有这么一句代码:
<img id="img_track_event_id" name="img_track_event_name" class="" src="images/bd_logo.png" width="300" height="128" onmousedown="CallCPlusPlus()" />
这个 img 元素的 id 值为
img_track_event_id,name 值为
img_track_event_name,然后我们可以尝试用 c++ 调用 js 代码:
document.getElementById("img_track_event_id").name
来返回我们需要返回的 img 控件的 name 值。
这里可以看到,我在浏览器地址栏输入了 js 代码,并点击 run js 后,响应按钮信息,从地址栏获取到了 js 代码,运行后获取到了 js 返回的值,并以弹框形式显示出来。
这里我感觉我已经讲述的非常详细了,其中关于如何自己在本地建立一个静态的 html 文件进行测试还没有细说,下一节将会讲到,其他如果还有不懂的地方,可以参看我的 demo。
四、js 调用 c++
在之前,我们已经实现了 c++ 调用 js 的功能,是通过 wke 封装好的runJS方法实现的。那么 js 调用 c++ 应该如何实现呢?
甚至有些人会问, js 调用 c++ 应该如何测试呢?
1. 创建测试网页
其实在上一节,我漏了很重要的一点没有讲述,那就是如何建立本地的网页测试。为什么要建立呢?因为我们要让 js 与 c++ 交互,就必定需要一个本地的可以测试的界面。这里我在软件运行路径下,建立了一个文件夹
Html,在这个文件夹中建立了文件
index.html,这个 html 文件里面主要就是有一个 img 元素,用来响应指定的 js 方法。
<img id="img_track_event_id" name="img_track_event_name" class="" src="images/bd_logo.png" width="300" height="128" onmousedown="CallCPlusPlus()" />
至于这个
index.html文件如何书写,可以参看我的 demo 项目,这里就不再赘述了(其实作为一个小白,这一块我也是遇到坑了的,不过可以看代码解决的问题,就不在博客里说了,代码里一切都有)。
需要说一下的是,如何使用 wke 载入本地的 html 文件呢:
webView->loadFile(_T("Html/index.html")); bool b = webView->isLoaded();
只需要让 wke 对象调用
loadFile方法即可,注意这里的路径设置,需要将 Html 文件夹放到程序的当前运行目录下(或许有些人会问,怎么知道程序的当前运行目录呢?一般都是 sln 的同级目录,不过也可以使用
GetCurrentDirectory函数来获取,我就是这么获取到的)。
到了现在,我们已经创建了测试的环境,并编写好了测试的网页元素,那么让我们开始吧!
2. 查看 wke 相关接口
首先,让我们看看 wke 提供的接口函数:WKE_API void jsBindFunction(const char* name, jsNativeFunction fn, unsigned int argCount); WKE_API void jsBindGetter(const char* name, jsNativeFunction fn); /*get property*/ WKE_API void jsBindSetter(const char* name, jsNativeFunction fn); /*set property*/
这里
jsBindFunction就是用来绑定 c++ 函数的方法。那么回调函数应该怎么写呢?
#define JS_CALL __fastcall typedef jsValue (JS_CALL *jsNativeFunction) (jsExecState es);
Got it!
我们现在有了绑定的函数,有了回调函数,只要琢磨清除用法就可以使用了!
那么该如何使用呢?
首先,在
index.html中写好我们触发的 js 函数:
<script type="text/javascript"> function CallCPlusPlus() { msgBox("点击图片由 js 调用 C++ 弹窗", "提示"); } </script>
这里需要解释下的就是,
msgBox我理解为就是一个标签,是一个什么标签呢?是用来在绑定的时候,让 wke 知道,哦,我在 js 中看到了
msgBox我就把它与某个 c++ 函数绑定起来,然后
msgBox函数里面自然可以传入参数。这样,通过这个标签,就完成了 js 和 c++ 对于同一个函数的认定响应。
然后,我们在 html 中编写能够触发我们这个 js 函数的元素:
<img id="img_track_event_id" name="img_track_event_name" class="" src="images/bd_logo.png" width="300" height="128" onmousedown="CallCPlusPlus()" />
注意这里选择了
onmousedown也就是鼠标单击行为触发此 js 函数,至此,我们已经完成了界面上需要做的事情。
再然后,我们到调用处,编写我们需要绑定的全局的 c++ 函数:
// 全局的 js 调用 c++ 的函数 jsValue JS_CALL jsMsgBox(jsExecState es) { const wchar_t *text = jsToStringW(es, jsArg(es, 0)); const wchar_t *title = jsToStringW(es, jsArg(es, 1)); SOUI::SMessageBox(NULL, text, title, MB_OK); return jsUndefined(); }
这里需要解释下,
jsMsgBox并不是我们之前在 js 函数中写的那个标签
msgBox。我们在 js 函数里的只是一个绑定时 c++ 和 js 双方都认定的一个标签而已,c++ 函数的名称是可以随便取的,但是函数返回值和参数必须与 wke 声明一致。
另外,传入的参数只有一个
es,wke 通过
jsToStringW(es, jsArg(es, 0))来解析出函数所需要的参数。这里我们解析出来了两个参数,并使用
SMessageBox函数显示出来,这里的
SMessageBox是 SOUI 封装的弹框类。
最后,我们将 js 中的标签
msgBox与 全局函数
jsMsgBox绑定起来即可:
// 绑定 js 函数,让 js 主动调用 C++ 函数 jsBindFunction("msgBox", jsMsgBox, 2);
这里
jsBindFunction函数,第一个参数的含义就是 js 函数中使用到的标签,第二个参数是需要绑定的 c++ 函数名,第三个参数是参数个数。
至此,我们完成了 c++ 部分的编写。
现在,我们运行程序,点击
run c++,进入测试界面,点击图片,触发 js 方法,调用 c++ 方法,弹框:
成功撒花!!!
五、总结
这些实现的方法都不是我做的。SOUI 界面库 是 SOUI 作者启程写的;
wke 浏览器内核是 BlzFans 写的;
js 和 c++ 互调方法是 Redrain 写的;
这里我只是摸索着他们的后路,将自己同样实现后的经验和感想分享。
最后,附上我的 demo 代码的 github 地址,希望能对有需要的同学有点启发,JsCplusplusInteractons。
相关文章推荐
- 基于wke封装的duilib的webkit浏览器控件,可以c++与js互交,源码及demo下载地址
- 基于wke封装的duilib的webkit浏览器控件,可以c++与js互交,源码及demo下载地址
- 基于wke封装的duilib的webkit浏览器控件,可以c++与js互交,源码及demo下载地址
- 如何实现使用C/C++直接调用系统默认浏览器?
- 基于wke封装的duilib的webkit浏览器控件,可以c++与js互交,源码及demo下载地址
- 如何实现IE中JS和VC之间的互相调用
- 如何实现IE中JS和VC之间的互相调用
- 如何实现IE中JS和VC之间的互相调用
- 如何调用DLL (基于Visual C++6.0的DLL编程实现)
- 使用SWIG实现C/C++与其他语言间的互相调用
- C程序和C++程序间如何互相调用
- 如何调用DLL (基于Visual C++6.0的DLL编程实现)
- webkit如何实现JS DOM binding—基于V8分析
- 用SWIG实现C++和Lua互相调用实例
- Dnode:基于Node.js给浏览器提供异步远程方法调用
- 如何利用JS实现对后台CS代码的调用
- 用SWIG实现C++和Lua互相调用实例
- 如何利用JS实现对后台CS代码的调用
- 使用SWIG实现C/C++与其他语言间的互相调用 zz
- webkit如何实现JS DOM binding—基于V8分析