一、下载CEF
-
CEF下载地址:https://cef-builds.spotifycdn.com/index.html
或https://bitbucket.org/chromiumembedded/cef/src/master/ -
选择对应系统的版本(本教程选择的是116.0.19)
-
CMake下载地址:https://cmake.org/download/
- 注意CEF版本,CEF116.0.19的需要cmake3.21以上版本才支持(本教程选择的是3.27.4)
二、CMake编译CEF源码
1、cmake源码
注意:低版本需要取消勾选USE_SENDBOX,否则会报错(如:vs17+CEF_92.0.26+cmake3.20.6),vs17支持的最高版本应该就到92了,有兴趣的可以每个版本都测一下
2、文件内容介绍
- ALL_BUILD:是cmake自动生成的辅助工程。
- cef_gtest:包含ceftests目标使用的Google C++测试框架。
- ceftests:包含执行CEF API的单元测试。
- cefclient:一个包含CEF各种API演示的浏览器程序Demo。
- cefsimple:一个创建CEF浏览器程序所需最少功能的Demo。
- libcef_dll_wrapper:对cef库的C++代码封装库。
- ALL_BUILD与ZERO_CHECK:是cmake自动生成的辅助工程。
三、运行示例
四、生成动态链接库
- 右键项目 libcef_dll_wrapper->属性->C/C+±>代码生成->运行库:改为“多线程调试 DLL (/MDd)”(如果是 release 版,则改为“多线程 DLL (/MD)”)
五、vs版本与cef版本
六、使用vs2022执行CMakeLists.txt
七、qt工程集成CEF
-
介绍
-
CEF是多进程的,浏览器在运行时会在window系统中创建多个进程,并且每个进程都是以命令行形式启动。CEF通过命令行启动进程,主要包含《浏览器进程》和《渲染进程》,一个进程程序启动后,通过CefApp接口将逻辑功能“注入”到CEF框架
-
CEF函数介绍
- CEF框架通过回调函数GetBrowserProcessHandler() 获取程序对象。
- OnContextInitialized()函数初始化浏览器,并最后创建出一个浏览器窗口。
- CefBrowserHost::CreateBrowser()函数需要一个CefClient对象(需要自己继承重写),该对象内包含了CEF框架的handle,例:
-
CefContextMenuHandler,主要用于处理 Context Menu 事件。
-
CefDialogHandler,主要用来处理对话框事件。
-
CefDisplayHandler,处理与页面状态相关的事件,如页面加载情况的变化,地址栏变化,标题变化等事件。
-
GetDragHandler,处理拖拽相关的事件,如从外边拖入浏览器事件
CefDownloadHandler,主要用来处理文件下载。 -
CefFocusHandler,主要用来处理焦点事件。
-
CefGeolocationHandler,用于申请 geolocation 权限。
-
CefJSDialogHandler,主要用来处理 JS 对话框事件。
-
CefKeyboardHandler,主要用来处理键盘输入事件。
-
CefLifeSpanHandler,主要用来处理与浏览器生命周期相关的事件,与浏览器对象的创建、销毁以及弹出框的管理。
- OnBeforePopup 方法控制弹出窗口的内容,位置等等。
-
CefLoadHandler,主要用来处理浏览器页面加载状态的变化,如页面加载开始,完成,出错等。
-
CefRenderHandler,主要用来处在在窗口渲染功能被关闭的情况下的事件。
-
CefRequestHandler,主要用来处理与浏览器请求相关的的事件,如资源的的加载,重定向等
-
-
-
集成流程
-
创建带有界面的qt工程。
-
在解决方案同级目录下新建CEF文件夹用于存放工程依赖文件
-
目录结构
- CEF/bin
该目录下存放CEF程序运行时所需要的所有动态库(.dll文件)。区分debug和release版本,从CEF二进制发行包根目录下拷贝过来,程序运行时需要将该目录下所有文件拷贝到exe同级目录下。 - CEF/include
该目录下存放CEF程序的头文件,从CEF二进制发行包根目录下拷贝过来。 - CEF/lib
该目录下存放CEF程序编译时依赖的静态链接库(.lib文件)。主要有libcef.lib和libcef_dll_wrapper.lib,注意区分debug和release版本。 - CEF/resources
该目录下存放CEF程序运行时所需要的资源文件,程序运行时需要将该目录下所有文件拷贝到exe同级目录下。
- CEF/bin
-
-
简单集成
-
参考test/cefsimple工程,现阶段使用的仍然是CEF本身的消息循环和显示窗口。
-
在工程同级目录下新建cefsimple文件夹,并从test/cefsimple工程目录下拷贝以下文件,并添加到工程:
- simple_app.cc
- simple_app.h
- simple_handler.cc
- simple_handler.h
- simple_handler_win.cc
-
属性配置
-
C/C++ -》常规-》附加包含目录:$(SolutionDir)CEF
-
C/C++ -》预处理器,添加以下宏(注意区分debug和release)
%(PreprocessorDefinitions) WIN32 _WINDOWS __STDC_CONSTANT_MACROS __STDC_FORMAT_MACROS _WIN32 UNICODE _UNICODE WINVER=0x0A00 _WIN32_WINNT=0x0A00 NTDDI_VERSION=NTDDI_WIN10_FE NOMINMAX WIN32_LEAN_AND_MEAN _HAS_EXCEPTIONS=0 PSAPI_VERSION=1 CEF_USE_SANDBOX CEF_USE_ATL _HAS_ITERATOR_DEBUGGING=0 CMAKE_INTDIR="Debug"
-
链接器-》常规-》附加库目录:$(SolutionDir)CEF\lib$(Configuration)
-
链接器-》常规-》输入:(注意区分debug和release)
libcef.lib libcef_dll_wrapper.lib
-
在SimpleHandler类中重写OnBeforePopup方法,否则每次点击链接都会在新的窗口中弹出。
-
main函数
#include "WebAPP.h" #include <QtWidgets/QApplication>#include "include/cef_command_line.h" //#include "include/cef_sandbox_win.h"//暂时没有用到#include "cefsimple/simple_app.h"int main(int argc, char *argv[]) {//暂时先注释掉,使用CEF窗口//QApplication a(argc, argv);//WebAPP w;//w.show();//1、获取HINSTANCEHINSTANCE hInstance = GetModuleHandle(NULL);//2、CEF命令行参数CefMainArgs main_args(hInstance);/* 3、* CefExecuteProcess函数创建进程,首次启动会创建主进程并返回负数* 当创建子进程时会再次调用该程序,并传入参数,例:“--type=renderer”,此时返回一个大于0的值并退出不再向下执行*/int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);if (exit_code >= 0){return exit_code;}//4、CEF全局配置CefSettings settings;settings.no_sandbox = true;//5、创建一个应用实例CefRefPtr<SimpleApp> app(new SimpleApp);//6、初始化CEFCefInitialize(main_args, settings, app.get(), nullptr);//7、CEF消息循环CefRunMessageLoop();//8、关闭CefShutdown();return 1;// a.exec(); }
-
-
-
qt工程集成
-
修改SimpleAPP类,集成自QOjbect,在OnContextInitialized()函数中添加创建窗口的信号。
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_ #define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_#include <QObject> #include "include/cef_app.h"// Implement application-level callbacks for the browser process. class SimpleApp : public QObject, public CefApp, public CefBrowserProcessHandler {Q_OBJECTpublic:SimpleApp();// CefApp methods:CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {return this;}// CefBrowserProcessHandler methods:void OnContextInitialized() override;CefRefPtr<CefClient> GetDefaultClient() override;signals:void sigCefInitialized();private:// Include the default reference counting implementation.IMPLEMENT_REFCOUNTING(SimpleApp); };#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_//cpp void SimpleApp::OnContextInitialized() {CEF_REQUIRE_UI_THREAD();//用触发信号的方式创建窗口emit sigCefInitialized();}
-
创建带UI的qt类WebAPP
-
WebAPP.h
#pragma once#include <QtWidgets/QMainWindow> #include "ui_WebAPP.h"#include "cefsimple/simple_app.h" #include "cefsimple/simple_handler.h"class WebAPP : public QMainWindow {Q_OBJECTpublic:WebAPP(QWidget *parent = nullptr);WebAPP(SimpleApp* app);~WebAPP();void setSimpleApp(SimpleApp* app);private slots:void slotCreateBrowserWindow();protected:void resizeEvent(QResizeEvent* event);private:Ui::WebAPPClass ui;SimpleApp* m_simpleApp = nullptr; };
-
WebAPP.cpp
#include "WebAPP.h"#include "cefsimple/simple_handler.h"WebAPP::WebAPP(QWidget *parent): QMainWindow(parent) {ui.setupUi(this); }WebAPP::WebAPP(SimpleApp* app): m_simpleApp(app) {}WebAPP::~WebAPP(){}void WebAPP::setSimpleApp(SimpleApp* app) {m_simpleApp = app;bool ret = QObject::connect(m_simpleApp, &SimpleApp::sigCefInitialized, this, &WebAPP::slotCreateBrowserWindow); }void WebAPP::resizeEvent(QResizeEvent* event) {if (SimpleHandler::GetInstance()){HWND wnd = SimpleHandler::GetInstance()->getBrowserWindowHandle();if (wnd){QRect qRect = this->centralWidget()->rect();::MoveWindow(wnd, qRect.x(), qRect.y(), qRect.width(), qRect.height(), 1);}} }void WebAPP::slotCreateBrowserWindow() {CefRefPtr<SimpleHandler> handler(new SimpleHandler(false));//浏览器配置CefBrowserSettings browser_settings;//打开的网址std::string url = "https://www.baidu.com";//浏览器窗口信息CefWindowInfo windowInfo;//windowInfo.SetAsPopup(NULL, "cefsimple");//获取嵌入窗口的句柄QString name = this->objectName();QObjectList objList = this->children();QWidget* centralWidget = this->centralWidget();HWND hwnd = (HWND)(this->centralWidget()->winId());CefWindowInfo c;RECT winRect;QRect rect = this->rect();CefRect cefRect;cefRect.x = rect.x();cefRect.y = rect.y();cefRect.width = rect.width();cefRect.height = rect.height();windowInfo.SetAsChild(hwnd, cefRect);//创建浏览器窗口CefBrowserHost::CreateBrowser(windowInfo, handler, url, browser_settings, nullptr, nullptr);}
-
-
main.cpp
//1、获取HINSTANCEHINSTANCE hInstance = GetModuleHandle(NULL);//2、CEF命令行参数CefMainArgs main_args(hInstance);/* 3、* CefExecuteProcess函数创建进程,首次启动会创建主进程并返回负数* 当创建子进程时会再次调用该程序,并传入参数,例:“--type=renderer”,此时返回一个大于0的值并退出不再向下执行*/int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);if (exit_code >= 0){return exit_code;}//4、CEF全局配置CefSettings settings;settings.no_sandbox = true;settings.multi_threaded_message_loop = true;//将CEF放在单独的线程上运行,而不是主线程//5、创建一个应用实例//CefRefPtr<SimpleApp> simpleApp(new SimpleApp);SimpleApp* simpleApp = new SimpleApp;QApplication a(argc, argv);//WebAPP w(simpleApp);//直接传入simleapp会导致webapp初始化失败WebAPP w;w.setSimpleApp(simpleApp);w.show();//6、初始化CEFCefRefPtr<SimpleApp> simpleApp2(simpleApp);CefInitialize(main_args, settings, simpleApp2.get(), nullptr);int ret = a.exec();//7、关闭//CefQuitMessageLoop();CefShutdown();return ret;
-
-
-
错误问题
-
错误:[0917/232542.473:FATAL:shutdown_checker.cc(30)] Check failed: !IsCefShutdown(). Object reference incorrectly held at CefShutdown
- debug模式下关闭程序报错,release正常,因为对象没有正确引用,退出时资源没有正确释放。
- 网页在退出的时候JavaScript可能还在执行,导致调用销毁顺序不一致。
- 注释掉SimpleHandler::OnBeforeClose函数内的//CefQuitMessageLoop();
- 参考博客:https://blog.csdn.net/Mingyueruya/article/details/122460285
-
错误:文件包含在偏移 0x120 处开始的字符,该字符在当前源字符集中无效(代码页 65001)。
-
vs添加:工具-》自定义-》命令-》菜单栏(选择文件)-》添加命令-》文件-》高级保存选项
-
文件-》高级保存选项-》修改文件编码格式,重新编译工程(依赖库也需要对应)。
-
-
错误:MSB8066 。。。。。自定义生成已退出,代码1。(报错在Microsoft.CppCommon.targets文件内)
- 由于工程中的文件编码格式由gb2312改为了utf-8导致(具体情况具体分析),修改编码保存重新生成,可能会提示没有权限保存(因为vs安装在C盘),可以保存到其他盘再以管理员替换vs安装目录下的该文件。
-
CEF集成参考博客:https://blog.csdn.net/paopao_wu/category_11518677.html?spm=1001.2014.3001.5482
65001)。
1. vs添加:工具-》自定义-》命令-》菜单栏(选择文件)-》添加命令-》文件-》高级保存选项
2. 文件-》高级保存选项-》修改文件编码格式,重新编译工程(依赖库也需要对应)。
-
错误:MSB8066 。。。。。自定义生成已退出,代码1。(报错在Microsoft.CppCommon.targets文件内)
- 由于工程中的文件编码格式由gb2312改为了utf-8导致(具体情况具体分析),修改编码保存重新生成,可能会提示没有权限保存(因为vs安装在C盘),可以保存到其他盘再以管理员替换vs安装目录下的该文件。
CEF集成参考博客:https://blog.csdn.net/paopao_wu/category_11518677.html?spm=1001.2014.3001.5482