一、下载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、获取HINSTANCE HINSTANCE 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、初始化CEF CefInitialize(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_OBJECT public: 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_OBJECT public: 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、获取HINSTANCE HINSTANCE 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、初始化CEF CefRefPtr<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