基于Duilib的CEF3中C++与JS函数互相调用示例

2023-01-20 11:34:40 3558人已围观 61已点赞 21人已收藏

简介本文向大家介绍一个C++实战项目:基于Duilib的CEF3中C++与JS函数互相调用示例,主要涉及开发技术点为DuiLib界面开发、谷歌浏览器CEF插件开发、C++与JavaScript的相互调用,具有一定的C++实战价值,感兴趣的朋友可以参考一下。

CEF简要介绍

CEF是一个基于Google Chromium的简单的框架。 它主要是作为一个内嵌浏览器嵌入到客户端应用程序中。

本文示例源码工程中已集成编译好的CEF源码,有需要的可在文末下载。

Duilib窗口中加载显示CEF

先来张效果图:

CEF,Duilib,CEF中C++与JS函数互相调用

首先在工程项目的入口函数中初始化CEF,其中CDuiCefApp类为继承 CefApp的类,详见源码。

	void* sandbox_info = NULL;
	CefMainArgs main_args(AfxGetInstanceHandle());
	CefRefPtr<CDuiCefApp>app(new CDuiCefApp);
	CefSettings settings;
	settings.no_sandbox = true;
	settings.background_color = CefColorSetARGB(255,255, 255, 255);
	settings.multi_threaded_message_loop = true; // 使用主消息循环
	CefInitialize(main_args, settings, app.get(), sandbox_info);

然后我们创建一个类为CMainWnd的Duilib窗口,然后在初始化窗口中加载CEF,其中CDuiMsgHandler类为继承CCefClient的类,详见源码。

if (msg.sType == _T("windowinit"))
	{
		CefWindowInfo windows_info;
		RECT rc;
		::GetClientRect(GetHWND(), &rc);
		rc.top = rc.top + 108;
		windows_info.SetAsChild(GetHWND(), rc);

		m_handler = new CDuiMsgHandler();
		CefBrowserSettings browser_settings;
		browser_settings.default_encoding.str = _T("GB2312");
		browser_settings.default_encoding.length = wcslen(_T("GB2312"));
		//std::string url = "https://www.baidu.com/";

		CString strHtmlPath = CCommon::GetModuleFilePath() + _T("");
		strHtmlPath.Format(_T("%shtml\\test.html"), CCommon::GetModuleFilePath());
		strHtmlPath.Replace(_T("\\"),_T("/"));
		std::string url = (LPCSTR)(CStringA)(strHtmlPath);

		CefBrowserHost::CreateBrowser(windows_info, m_handler, "file:///" + url, browser_settings, NULL,NULL);
	}

test.html文件为自写的测试文件,内容如下:

<!DOCTYPE HTML>
<html>
	<head>
		<meta charset="utf-8" />
		<title>基于Duilib的CEF3中C++与JS函数互相调用示例</title>
	</head>
	<body style="margin-left: 30px;">
		<p style="font-weight: bold;">Js调用C++函数</p>
		<div>
			<p><button onclick="OnJsCallCppTest1();">Js调用C++函数(参数为空)</button></p>
			<p><button onclick="OnJsCallCppTest2();">Js调用C++函数(参数带整型)</button></p>
			<p><button onclick="OnJsCallCppTest3();">Js调用C++函数(参数带字符串)</button></p>
		</div>
	</body>
	<script type="text/javascript">
		// C++调用JS
		function CallJavaScript(value) {
			alert('C++调用JS: ' + value)
		}

		// Js调用C++
		function OnJsCallCppTest1() {
			window.g_JsCallCppTest1();
		}

		function OnJsCallCppTest2() {
			window.g_JsCallCppTest2(10, 20);
		}

		function OnJsCallCppTest3() {
			window.g_JsCallCppTest3('C++实战网(www.cppszw.com)');
		}
	</script>
</html>

C++调用Js函数

C++调用Js函数, 使用CefBrowser的GetMainFrame()获取browser的mainframe,再调用他的ExecuteJavaScript方法来调用页面的js函数, 此种方法比较简单:

// JS函数调用
m_handler->GetBrowser()->GetMainFrame()->ExecuteJavaScript(_T("CallJavaScript('C++实战网(www.cppszw.com)')"), m_handler->GetBrowser()->GetMainFrame()->GetURL(), 0);

CEF,Duilib,CEF中C++与JS函数互相调用

Js调用C++

想要C++和Js交互,我们只需要实现以下接口:

  • CefRenderProcessHandler的OnContextCreated
  • CefV8Handler的Execute
#pragma once
#include "../stdafx.h"
#include "include/cef_app.h"

class CDuiCefApp :
	public CefApp,
	public CefBrowserProcessHandler,
	public CefRenderProcessHandler,
	public CefV8Handler
{
public:
	CDuiCefApp();
	virtual ~CDuiCefApp(void) override;

	// CefApp methods
	virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override;
	// 通过返回值获取render线程,必须重写
	virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override;

	// 上下文对象创建后,进入这个函数
	virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override;

	virtual void OnContextInitialized() override;

	/**
	* 执行JS函数
	*
	* @param funcName		函数名称
	* @param object			调用对象
	* @param retval			返回值
	* @param exception		异常信息
	*
	**/
	virtual bool Execute(const CefString& funcName,
		CefRefPtr<CefV8Value> object,
		const CefV8ValueList& arguments,
		CefRefPtr<CefV8Value>& retval,
		CefString& exception) override;

	// 发送消息到主窗口
	bool SendMsgToMainWnd(CefString strMsg);
private:
	HWND m_hWndMain;
	// Include the default reference counting implementation
	IMPLEMENT_REFCOUNTING(CDuiCefApp);
};

然后我们在CDuiCefApp::OnContextCreated()函数中绑定自定义的Js中对应的C++函数即可:

void CDuiCefApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
	// The var type can accept all object or variable
	CefRefPtr<CefV8Value> window = context->GetGlobal();

	m_hWndMain = ::FindWindow(NULL, TEXT("DuilibCefDemo"));

	// 绑定函数
	CefRefPtr<CefV8Handler> pJsHandler = this;
	CefRefPtr<CefV8Value> JsCallCppTest1 = CefV8Value::CreateFunction("JsCallCppTest1", pJsHandler);
	CefRefPtr<CefV8Value> JsCallCppTest2 = CefV8Value::CreateFunction("JsCallCppTest2", pJsHandler);
	CefRefPtr<CefV8Value> JsCallCppTest3 = CefV8Value::CreateFunction("JsCallCppTest3", pJsHandler);
	// 特别注意:C++函数名不能与Js函数名挂接相同
	window->SetValue("g_JsCallCppTest1", JsCallCppTest1, V8_PROPERTY_ATTRIBUTE_NONE);
	window->SetValue("g_JsCallCppTest2", JsCallCppTest2, V8_PROPERTY_ATTRIBUTE_NONE);
	window->SetValue("g_JsCallCppTest3", JsCallCppTest3, V8_PROPERTY_ATTRIBUTE_NONE);
}

这里需要特别注意的是:C++函数名不能与Js函数名挂接相同,如示例中C++函数名为JsCallCppTest1,则Js函数绑定的时候设为g_JsCallCppTest1,否则Js调用将会不成功。

最后,我们在CDuiCefApp::Execute()函数中编写对应函数的响应代码即可:

// 执行JS函数
bool CDuiCefApp::Execute(const CefString& funcName,
	CefRefPtr<CefV8Value> object,
	const CefV8ValueList& arguments,
	CefRefPtr<CefV8Value>& retval,
	CefString& exception)
{
	// Js调用C++函数
	if (funcName == "JsCallCppTest1")
	{
		SendMsgToMainWnd(L"Js调用C++函数:JsCallCppTest1(参数为空)");
		return true;
	}
	else if (funcName == "JsCallCppTest2")
	{
		if (arguments.size() == 2)
		{
			CString sTemp = _T("");
			sTemp.Format(_T("Js调用C++函数:JsCallCppTest2(%d,%d)"), arguments[0]->GetIntValue(), arguments[1]->GetIntValue());
			SendMsgToMainWnd(sTemp.GetBuffer());
			return true;
		}
	}
	else if (funcName == "JsCallCppTest3")
	{
		if (arguments.size() == 1)
		{
			SendMsgToMainWnd(arguments[0]->GetStringValue());
			return true;
		}
	}
	return false;
}

点击Js调用C++函数(参数带字符串)中,可看到效果如下:

CEF,Duilib,CEF中C++与JS函数互相调用

源码下载
  • 最近更新:   2022-06-09开发环境:   Visual Studio 2015
  • 源码大小:   64.29MB下载次数:  28 

更多为你推荐