CEF环境下如何通过JavaScript调用C++回调函数?
可以将JS的alert()替换为调用C++函数,以下是具体实现方式
CEF支持JS与C的双向通信,主要通过**注入绑定C逻辑的自定义JS对象**,或者进程间消息传递两种方式实现,下面分别给出具体方案:
方案一:注入自定义JS对象(直接调用C++方法)
这种方式可以让JS直接调用绑定到全局对象的C++方法,步骤如下:
- 实现
CefRenderProcessHandler类,重写OnContextCreated方法,在浏览器上下文初始化时注入自定义V8对象:
class MyRenderProcessHandler : public CefRenderProcessHandler { public: void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override { // 获取全局window对象 CefRefPtr<CefV8Value> globalObj = context->GetGlobal(); // 创建自定义对象 CefRefPtr<CefV8Value> bridgeObj = CefV8Value::CreateObject(nullptr, nullptr); // 绑定C++回调方法到自定义对象 CefRefPtr<CefV8Handler> handler = new MyV8Handler(); bridgeObj->SetValue("sendRectData", CefV8Value::CreateFunction("sendRectData", handler), V8_PROPERTY_ATTRIBUTE_NONE); // 将自定义对象挂载到window下 globalObj->SetValue("MyCppBridge", bridgeObj, V8_PROPERTY_ATTRIBUTE_NONE); } // 实现V8回调处理器 class MyV8Handler : public CefV8Handler { public: bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) override { if (name == "sendRectData" && arguments.size() == 2) { // 从JS参数中获取rect的x和y值 double x = arguments[0]->GetDoubleValue(); double y = arguments[1]->GetDoubleValue(); // 这里编写你的C++业务逻辑,比如打印、存储或处理坐标 printf("Received rect coordinates from JS: x=%.2f, y=%.2f\n", x, y); return true; } return false; } }; IMPLEMENT_REFCOUNTING(MyRenderProcessHandler); };
- 修改你的
ExecuteJavaScript代码,将alert替换为调用自定义JS对象的方法:
(*frame).GetMainFrame()->ExecuteJavaScript( "const elem = document.getElementsByClassName(\"my_class\")[0];const rect = elem.getBoundingClientRect(); window.MyCppBridge.sendRectData(rect.x, rect.y);", "", 1 );
方案二:使用CEF进程间消息传递
如果你的C++逻辑运行在浏览器进程(而非渲染进程),可以通过消息传递实现:
- 在JS中调用
cefQuery发送数据到渲染进程:
(*frame).GetMainFrame()->ExecuteJavaScript( "const elem = document.getElementsByClassName(\"my_class\")[0];const rect = elem.getBoundingClientRect();" "cefQuery({request: JSON.stringify({x: rect.x, y: rect.y}), onSuccess: ()=>{}, onFailure: ()=>{}});", "", 1 );
- 在
CefRenderProcessHandler中处理JS发送的查询,并转发到浏览器进程:
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) override { if (message->GetName() == "SendRectData") { // 从消息中获取坐标数据 CefRefPtr<CefListValue> args = message->GetArgumentList(); double x = args->GetDouble(0); double y = args->GetDouble(1); // 处理C++逻辑 printf("Received rect coordinates: x=%.2f, y=%.2f\n", x, y); return true; } return false; } bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool user_gesture, bool is_redirect) override { // 注册cefQuery回调 frame->ExecuteJavaScript( "window.cefQuery = function(query) {" " CefQuery({request: query.request, onSuccess: query.onSuccess, onFailure: query.onFailure});" "};", "", 1 ); return false; }
注意事项
- CEF分为渲染进程和浏览器进程,V8对象绑定操作必须在渲染进程的主线程中执行;如果C++逻辑涉及UI操作,建议放在浏览器进程中处理,通过进程间消息传递数据。
- 确保在CEF初始化时设置自定义的
CefRenderProcessHandler,否则注入或消息监听不会生效。
内容的提问来源于stack exchange,提问作者Optimus1
相关产品推荐
相关产品推荐

