一、引言

我们要实现一个强大的浏览器控件,必须要能够实现 C++ 与 JavsScript 的互相调用。

于是,在我研究了:

菜鸟与 cef 的邂逅之旅(一):cef 源码获取与编译
菜鸟与 cef 的邂逅之旅(二):Soui 中接入 Cef3 的实现

这些主题之后,研究 Cef3 中 C++ 与 JavaScript 之间的互相调用也是顺水推舟的事情了。

这里,我依然参照了大神 蓝先生 的示例代码(根据此示例代码在上一篇博客中自行建立的项目),认真研究了下如何在基于 Soui 界面库的 Cef3 机制中实现 C++ 和 JavaScript 互相调用的方法。

二、Cef3: C++ 调用 JavaScript

C++ 调用 JavaScript 可以说是比较简单的。看一下 Cef3 官方文档:

CefBrowser 和 CefFrame 对象被用来发送命令给浏览器以及在回调函数里获取状态信息。每个 CefBrowser 对象包含一个主 CefFrame 对象,主 CefFrame 对象代表页面的顶层 frame;同时每个 CefBrowser 对象可以包含零个或多个的 CefFrame 对象,分别代表不同的子 Frame。

CefBrowser 和 CefFrame 对象在 Browser 进程和 Render 进程都有对等的代理对象。

根据文档的了解,我们发现,我们可以通过 CBrowser 的实例得到其顶层 CefFrame 对象,然后通过它来调用 JavaScript 代码。

以下是大神 蓝先生封装的 SCefWebView 控件中的 ExecJavaScript 方法:

void SCefWebView::ExecJavaScript(const SStringW& js)
{if (!m_pBrowserHandler){return;}CefRefPtr<CefBrowser> pb = m_pBrowserHandler->GetBrowser();if ( pb.get() ){   CefRefPtr<CefFrame> frame = pb->GetMainFrame();if ( frame ){frame->ExecuteJavaScript((LPCWSTR)js, L"", 0);}}
}

我们了解了 C++ 调用 JavaScript 方法的原理之后,只需要在指定的事件里找到这个 Cef 控件,通过控件调用 ExecJavaScript 方法即可:

// 执行 JavaScript 代码
bool CMainDlg::OnRunJavaScript(SOUI::EventArgs *pEvt)
{SOUI::SEdit *pEditURL = FindChildByName2<SOUI::SEdit>(L"edit_input_url");SOUI::SCefWebView *pCefBrowser = FindChildByName2<SOUI::SCefWebView>(L"cef_browser");if (pEditURL && pCefBrowser) {SStringW strJs = pEditURL->GetWindowText();pCefBrowser->ExecJavaScript(strJs);}return true;
}

这里,用户通过在地址栏输入 JavaScript 代码之后,点击 Run Js 按钮之后执行该处代码(具体绑定过程看 SOUI 教程)。

以下是运行结果:

三、Cef3: JavaScript 调用 C++

上面我们已经实现了 C++ 调用 JavaScript 的功能。

而如何实现 JavaScript 对于 C++ 的调用呢?

通过认真参看代码逻辑,我了解到了这样的实现:

1. 定义 SOUI::SCefWebView 的窗口通知事件

以下是文件 ExtendEvents.h 的内容:

namespace SOUI
{
#define EVT_CEFWEBVIEW_BEGIN        (EVT_EXTERNAL_BEGIN + 900)
#define EVT_WEBVIEW_NOTIFY          (EVT_CEFWEBVIEW_BEGIN+0)class EventWebViewNotify : public TplEventArgs<EventWebViewNotify>{SOUI_CLASS_NAME(EventWebViewNotify, L"on_webview_notify")public:EventWebViewNotify(SObject *pSender) :TplEventArgs<EventWebViewNotify>(pSender) {}enum { EventID = EVT_WEBVIEW_NOTIFY };SStringW         MessageName;SArray<SStringW> Arguments;};};// namespace SOUI

这里定义了通知消息类型 EventWebViewNotify,其中定义了通知事件 Id 为 EVT_WEBVIEW_NOTIFY,并且声明了两个成员变量,一个用于存储响应 JavaScript 函数名称的 MessageName,另一个则是存储 JavaScript 函数调用的参数的数组变量。

2. 子进程 WebProcess 中初始化声明全局函数(JavaScript 调用),JavaScript 可以调用该函数达到调用 C++ 函数的目的

以下是 SubProcessClientApp.cpp 中对于 JavaScript 可调用全局函数的定义:

void SubProcessClientApp::OnContextCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context)
{CefRefPtr<CefV8Value> object = context->GetGlobal();CefRefPtr<CefV8Handler> handler = new HtmlEventHandler();CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("HandleEvent", handler);object->SetValue("HandleEvent", func, V8_PROPERTY_ATTRIBUTE_NONE);
}

可以看到,我们在其中将这个 JavaScript 可以全局调用的函数命名为了 HandleEvent,那么之后我们在 JavaScript 代码中就要使用这个函数进行调用 C++ 的目的。

以下是 HTMLEventHandler.cpp 中对于 JavaScript 执行函数的定义:

bool HtmlEventHandler::Execute(const CefString& name,CefRefPtr<CefV8Value> object,const CefV8ValueList& arguments,CefRefPtr<CefV8Value>& retval,CefString& exception)
{if (name != "HandleEvent" || arguments.size() == 0){return true;}CefRefPtr<CefBrowser> browser =CefV8Context::GetCurrentContext()->GetBrowser();CefRefPtr<CefProcessMessage> message =CefProcessMessage::Create(arguments[0]->GetStringValue());message->GetArgumentList()->SetSize(arguments.size() - 1);for (size_t i = 1; i < arguments.size(); ++i){message->GetArgumentList()->SetString(i - 1, arguments[i]->GetStringValue());}browser->SendProcessMessage(PID_BROWSER, message);return false;
}

可以看到,同样的,我们仔细甄选了 HandleEvent 的函数调用信息,然后将其发送给了我们的浏览器实例 Browser 进程,之后我们在 Browser 进程中的 BrowserHandler::OnProcessMessageReceived() 函数中可以接收到该调用信息,该函数又将信息发送给了子类实现 SCefWebView::OnBrowserMessage() 函数中:

bool SCefWebView::OnBrowserMessage(CefRefPtr<CefBrowser> browser,CefProcessId source_process,CefRefPtr<CefProcessMessage> message){EventWebViewNotify evt(this);evt.MessageName = message->GetName().ToWString().c_str();CefRefPtr<CefListValue> arg = message->GetArgumentList();for (int i = 0; i < arg->GetSize(); ++i){SStringW str = arg->GetString(i).ToWString().c_str();evt.Arguments.Add(str);}return !!FireEvent(evt);}

可以看到,我们将收到的 JavaScript 调用信息转化成了我们之前定义的 EventWebViewNotify 通知类型,这里经过一系列的参数处理,然后发送响应到了 UI 层。

3. 使用我们之前定义的全局函数 HandleEvent 实现 JavaScript 对于 C++ 的调用:

以下是 test.htm 的实现:

<html>
<head><meta charset="utf-8" />
</head><script>function callCpp() {window.HandleEvent("ClickButton", document.getElementById("input_text").value);}
</script><body><input type="text" id="input_text" stype="width:200px;height:50" value="hello cef3" /><button type="button" onclick="callCpp()">Call C++</button>
</body></html>

可以看到,我们点击 Call C++ 的 button,响应了 callCpp() 的 JavaScript 函数,在这个 JavaScript 函数中,我们调用了全局函数 HandleEvent,并往里面传递了函数名 ClickButton,还传递了页面元素 input_text 的值。

3. 绑定 cef 浏览器控件响应自定义的 EVT_WEBVIEW_NOTIFY 消息,将接收到的 JavaScript 函数传递的函数名和参数名显示出来:

以下是 SOUI 中绑定浏览器消息的实现:

SOUI::SCefWebView *pCefBrowser = FindChildByName2<SOUI::SCefWebView>(L"cef_browser");if (pCefBrowser) {pCefBrowser->GetEventSet()->subscribeEvent(EVT_WEBVIEW_NOTIFY, Subscriber(&CMainDlg::OnWebViewNotify, this));}

以下是界面层中显示 JavaScript 调用信息的实现:

// 获得 Js 的通知信息,显示结果
bool CMainDlg::OnWebViewNotify(SOUI::EventArgs *pEvt)
{SOUI::EventWebViewNotify *pev = (SOUI::EventWebViewNotify*)pEvt;SStringW args;for (size_t i = 0; i < pev->Arguments.GetCount(); ++i) {if (i != 0) {args += _T(", ");}args += pev->Arguments[i];}SOUI::SMessageBox(m_hWnd, args, pev->MessageName, MB_OK);return true;
}

最后,让我们看看最后的运行结果吧:

^_^
完结!撒花!

四、总结

基于 SOUI 的 Cef3 中 C++ 与 JavaScript 的互相调用其实并不复杂,其根本原理还是 Browser 进程与 Render 进程的互相通信。

其中 Browser 进程向 Render 进程发送消息,Render 进程响应,这就是 C++ 调用 JavaScript;
同样的,Render 进程向 Browser 进程发送消息,Browser 进程响应,这就是 JavaScript 调用 C++;
当然内部有着复杂的实现,不过原理就是这样。

最后附上本博客的实验代码:

wangying2016/Cef3-Soui-Demo

Cef3 之路还很漫长,想要灵活运用还有很长的距离要走:

最后再次感谢大神蓝先生对于我的帮助

To be Stronger!

菜鸟与 cef 的邂逅之旅(三):Cef3 中 C++ 与 JavaScript 的互相调用相关推荐

  1. HALCON学习之旅(三)

    HALCON学习之旅(三) 文章目录 HALCON学习之旅(三) 1.创建自适应图形窗口 2.霍夫变换寻找图像直线 1.创建自适应图形窗口 原因:默认的图形窗口尺寸为512*512.当图像变量尺寸与图 ...

  2. 打怪升级之小白的大数据之旅(三十一)<JavaSE总结>

    打怪升级之小白的大数据之旅(三十) JavaSE总结 引言 Java这只小怪物我们已经练级差不多了,明天我们将进入新的旅程了,所以,我要对前面的整个JavaSE知识点进行总结,就像积攒够了经验升级一样 ...

  3. 图谱实战 | 知识图谱在美团搜索酒旅场景认知中的应用

    分享嘉宾:陈骐 美团 高级算法专家 编辑整理:毛佳豪 中国平安浙江分公司(实习) 出品平台:DataFunTalk 导读:知识图谱凭借能够以图模型描述知识和世界万物关联关系的特性,在各行业领域大放异彩 ...

  4. ACMNO.33 C语言-最大值3 分别用函数和带参的宏,从三个数中找出最大的数。

    题目描述 分别用函数和带参的宏,从三个数中找出最大的数. 输入 3个实数 输出 最大的数,输出两遍,先用函数,再用宏. 保留3位小数. 样例输入 1 2 3 样例输出 3.000 3.000 来源/分 ...

  5. R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、编写自定义函数在三线表中添加p值

    R语言使用table1包绘制(生成)三线表.使用单变量分列构建三线表.编写自定义函数在三线表中添加p值 目录

  6. 三线表是什么?R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、编写自定义三线表结构(将因子变量细粒度化重新构建三线图)、编写自定义函数在三线表中添加p值

    三线表是什么?R语言使用table1包绘制(生成)三线表.使用单变量分列构建三线表.编写自定义三线表结构(将因子变量细粒度化重新构建三线图).编写自定义函数在三线表中添加p值 目录

  7. Android WebView 和 javaScript的互相调用(三)

    原文出处:http://motalks.cn/2016/08/27/Android-WebView-JavaScript-2/ WebView相关阅读 Android WebView 和 javaSc ...

  8. python三引号 内部变量_python在三引号中使用变量

    1.定义和运算: 变量名 = 值 定义变量举例: # 定义一个变量 myCar = "比亚迪F0" # 输出一个变量 print(myCar) 变量之间的简单运算举例: price ...

  9. 笔记三 vue中封装复用 过滤器 自定义组件 vue中component选项

    题外话: npm install -g express-generator npm安装express框架 封装复用 Vue中关于封装复用的内容,属于Vue中的进阶知识,在实战中对开发者的抽象和泛化能力 ...

最新文章

  1. kettle读取不到oracle,kettle链接Oracle数据库,百试不爽!
  2. 谷歌又一部门震荡:半年2名副总出走,开发团队只剩一半
  3. 再创新高:DeepMind一年烧掉6.5亿美元,谷歌却挥手免除15亿债务
  4. Java print流简介
  5. 巧用css的border属性完成对图片编辑功能的性能优化
  6. IPv6与IPv4的区别
  7. 计算机网络之数据链路层:1、概述
  8. Kali学习笔记5:被动信息收集工具集
  9. linux gd结构体,U-Boot中gd的定义和使用
  10. java 五大原则_面向对象五大原则
  11. 设置不定宽高的元素垂直水平居中
  12. 基于uFUN开发板的RGB调色板
  13. mysql删除索引_MySQL 索引详解
  14. wifi 广告推送 小记
  15. 大唐杯比赛辅导,国一选手
  16. 博客系统 - 系统简介与首页设计
  17. 如何解决图片路径是中文名称的问题
  18. Homekit智能家居DIY设备一智能灯泡
  19. 单片机c语言 软件抗干扰,单片机软件抗干扰的几种常见方法
  20. FPGA系统性学习笔记连载_Day8【4位乘法器、4位除法器设计】 【原理及verilog实现、仿真】篇

热门文章

  1. 【学习日志】2022.08.26 C#单例模式 Tostring Utils
  2. 微信WiFi认证的解决方案
  3. 3款好用的知识库软件,帮你解决私有化部署,老板抓紧收藏
  4. C++_DOS命令下_猫狗大战小游戏(初识QT小练习)
  5. 功能测试与抓包工具Fiddler(http与fiddler)
  6. 线性代数复习总结——基本概念
  7. 精彩WAP之旅--上海热线WAP站
  8. css图片精灵定位_CSS精灵图片(CSS sprite)使用心得(转)
  9. surface pro 4 发热抖屏的解决方法
  10. 【电子器件笔记6】三极管(BJT)参数和选型