一、下载CEF

  1. CEF下载地址:https://cef-builds.spotifycdn.com/index.html
    或https://bitbucket.org/chromiumembedded/cef/src/master/

  2. 选择对应系统的版本(本教程选择的是116.0.19)
    在这里插入图片描述

  3. CMake下载地址:https://cmake.org/download/

    1. 注意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自动生成的辅助工程。

三、运行示例

在这里插入图片描述在这里插入图片描述

四、生成动态链接库

  1. 右键项目 libcef_dll_wrapper->属性->C/C+±>代码生成->运行库:改为“多线程调试 DLL (/MDd)”(如果是 release 版,则改为“多线程 DLL (/MD)”)

五、vs版本与cef版本

六、使用vs2022执行CMakeLists.txt

七、qt工程集成CEF

  1. 介绍

    1. CEF是多进程的,浏览器在运行时会在window系统中创建多个进程,并且每个进程都是以命令行形式启动。CEF通过命令行启动进程,主要包含《浏览器进程》和《渲染进程》,一个进程程序启动后,通过CefApp接口将逻辑功能“注入”到CEF框架
      在这里插入图片描述

    2. CEF函数介绍

      1. CEF框架通过回调函数GetBrowserProcessHandler() 获取程序对象。
      2. OnContextInitialized()函数初始化浏览器,并最后创建出一个浏览器窗口。
      3. CefBrowserHost::CreateBrowser()函数需要一个CefClient对象(需要自己继承重写),该对象内包含了CEF框架的handle,例:
        1. CefContextMenuHandler,主要用于处理 Context Menu 事件。

        2. CefDialogHandler,主要用来处理对话框事件。

        3. CefDisplayHandler,处理与页面状态相关的事件,如页面加载情况的变化,地址栏变化,标题变化等事件。

        4. GetDragHandler,处理拖拽相关的事件,如从外边拖入浏览器事件
          CefDownloadHandler,主要用来处理文件下载。

        5. CefFocusHandler,主要用来处理焦点事件。

        6. CefGeolocationHandler,用于申请 geolocation 权限。

        7. CefJSDialogHandler,主要用来处理 JS 对话框事件。

        8. CefKeyboardHandler,主要用来处理键盘输入事件。

        9. CefLifeSpanHandler,主要用来处理与浏览器生命周期相关的事件,与浏览器对象的创建、销毁以及弹出框的管理。

          1. OnBeforePopup 方法控制弹出窗口的内容,位置等等。
        10. CefLoadHandler,主要用来处理浏览器页面加载状态的变化,如页面加载开始,完成,出错等。

        11. CefRenderHandler,主要用来处在在窗口渲染功能被关闭的情况下的事件。

        12. CefRequestHandler,主要用来处理与浏览器请求相关的的事件,如资源的的加载,重定向等

  2. 集成流程

    1. 创建带有界面的qt工程。

    2. 在解决方案同级目录下新建CEF文件夹用于存放工程依赖文件

      1. 目录结构

        • 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同级目录下。
    3. 简单集成

      1. 参考test/cefsimple工程,现阶段使用的仍然是CEF本身的消息循环和显示窗口。

      2. 在工程同级目录下新建cefsimple文件夹,并从test/cefsimple工程目录下拷贝以下文件,并添加到工程:

        • simple_app.cc
        • simple_app.h
        • simple_handler.cc
        • simple_handler.h
        • simple_handler_win.cc
      3. 属性配置

        1. C/C++ -》常规-》附加包含目录:$(SolutionDir)CEF

        2. 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"
          
        3. 链接器-》常规-》附加库目录:$(SolutionDir)CEF\lib$(Configuration)

        4. 链接器-》常规-》输入:(注意区分debug和release)

          libcef.lib
          libcef_dll_wrapper.lib
          
        5. 在SimpleHandler类中重写OnBeforePopup方法,否则每次点击链接都会在新的窗口中弹出。

        6. 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();
          }
          
          
    4. qt工程集成

      1. 修改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();
        
        }
        
      2. 创建带UI的qt类WebAPP

        1. 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;
          };
          
        2. 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);
          
          }
          
          
      3. 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;
        
  3. 错误问题

    1. 错误:[0917/232542.473:FATAL:shutdown_checker.cc(30)] Check failed: !IsCefShutdown(). Object reference incorrectly held at CefShutdown

      1. debug模式下关闭程序报错,release正常,因为对象没有正确引用,退出时资源没有正确释放。
      2. 网页在退出的时候JavaScript可能还在执行,导致调用销毁顺序不一致。
      3. 注释掉SimpleHandler::OnBeforeClose函数内的//CefQuitMessageLoop();
      4. 参考博客:https://blog.csdn.net/Mingyueruya/article/details/122460285
    2. 错误:文件包含在偏移 0x120 处开始的字符,该字符在当前源字符集中无效(代码页 65001)。

      1. vs添加:工具-》自定义-》命令-》菜单栏(选择文件)-》添加命令-》文件-》高级保存选项

      2. 文件-》高级保存选项-》修改文件编码格式,重新编译工程(依赖库也需要对应)。

    3. 错误:MSB8066 。。。。。自定义生成已退出,代码1。(报错在Microsoft.CppCommon.targets文件内)

      1. 由于工程中的文件编码格式由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. 文件-》高级保存选项-》修改文件编码格式,重新编译工程(依赖库也需要对应)。
  1. 错误:MSB8066 。。。。。自定义生成已退出,代码1。(报错在Microsoft.CppCommon.targets文件内)

    1. 由于工程中的文件编码格式由gb2312改为了utf-8导致(具体情况具体分析),修改编码保存重新生成,可能会提示没有权限保存(因为vs安装在C盘),可以保存到其他盘再以管理员替换vs安装目录下的该文件。

CEF集成参考博客:https://blog.csdn.net/paopao_wu/category_11518677.html?spm=1001.2014.3001.5482

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐