Qt6内嵌CEF

一、下载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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/417557.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大数据界面:客户又又又要求科技感了,如何破?

如果你问客户想要什么风格&#xff0c;大部分脱口而出科技感&#xff0c;不仅要求静态页&#xff0c;而且还要求动态效果&#xff0c;炫酷动画&#xff0c;贝格前端工场结合多个项目经历&#xff0c;帮助友友们梳理如何让界面科技动感。 一、没有科技感背后的潜台词 客户说大数…

SI523 替代CLRC522/RC523,软硬件开发资料

​si523是一个 13.56MHz 的非接触式读写器芯片&#xff0c;支持 ISO/IEC 14443 A/B/MIFARE协议。内部集成低功耗自动寻卡与定时唤醒功能&#xff0c;可编程寻卡时间间隔&#xff0c;寻卡过程无需 MCU 操作&#xff0c;寻卡成功中断唤醒或定时唤醒 MCU 以实现低功耗。 产品特性…

笑营宝高校选修课报名考勤系统源码开发方案

一、项目背景与目标 &#xff08;一&#xff09;项目背景 随着高等教育的普及和教学模式的不断创新&#xff0c;高校选修课程体系日趋复杂多变。学生对课程选择的自由度提高&#xff0c;使得传统的选课和考勤管理方式变得繁琐且效率低下。目前&#xff0c;许多高校仍然采用纸…

金三银四,自动化测试面试题精选【美团二面】

面试一般分为技术面和hr面&#xff0c;形式的话很少有群面&#xff0c;少部分企业可能会有一个交叉面&#xff0c;不过总的来说&#xff0c;技术面基本就是考察你的专业技术水平的&#xff0c;hr面的话主要是看这个人的综合素质以及家庭情况符不符合公司要求&#xff0c;一般来…

Nginx之rewrite重写功能

目录 一、rewrite概述 1、rewrite功能 2、跳转场景 二、标准配置指令 1、rewrite日志记录指令 2、未初始化变量告警日志记录指令 3、rewrite 指令 3.1 正则表达式 三、rewrite模块使用实例 1.基于域名的跳转 2.基于客户端 IP 访问跳转 3. 基于旧域名跳转到新域名后面…

Spring Cloud2022之OpenFeign使用以及部分源码分析

OpenFeign使用 Feign和OpenFeign Feign是Netflix开发的⼀个轻量级RESTful的HTTP服务客户端&#xff0c;可以使用⽤它来发起请求&#xff0c;进行远程调用。Fegin是以Java接口注解的⽅式调⽤Http请求&#xff0c;而不是像RestTemplate那样&#xff0c;在Java中通过封装HTTP请求…

【茶话数据结构】查找最短路径——Dijkstra算法详解(保姆式详细图解,步步紧逼,保你学会)

&#x1f4af; 博客内容&#xff1a;【茶话数据结构】查找最短路径——Dijkstra算法详解 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f989;所属专栏&#xff1a;数据结构笔记 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实…

d3dcompiler_47.dll丢失的解决方法

d3dcompiler_47.dll 是一个属于 Microsoft DirectX 软件组件的动态链接库 (DLL) 文件。DirectX 是微软开发的一套广泛应用于 Windows 操作系统上的应用程序接口 (API)&#xff0c;主要用于处理多媒体、特别是与游戏和高级图形相关的任务&#xff0c;例如二维 (2D) 和三维 (3D) …

Aigtek高精度电流源仪器设计规范

高精度电流源仪器是一种用于产生和测量精确电流的设备&#xff0c;广泛应用于电子、通信、自动控制等领域。为了确保仪器的性能和可靠性&#xff0c;设计过程中需要遵循一些规范。 电流源仪器的设计要注重稳定性。稳定性是保证仪器输出电流精度的关键因素。设计过程中应选择高精…

稀疏图带负边的全源最短路Johnson算法

BellmanFord算法 Johnson算法解决的问题 带负权的稀疏图的全源最短路 算法流程 重新设置的每条边的权重都大于或等于0&#xff0c;跑完Djikstra后得到的全源最短路&#xff0c;记得要还原&#xff0c;即&#xff1a;f(u,v) d(u,v) - h[u] h[v] 例题

vue 解决:点击左侧相同菜单,右侧页面不重新加载的问题

1、问题描述&#xff1a; 其一、需求为&#xff1a; 无论是通过路由组件形成的平台管理系统&#xff0c;还是通过文件配置形成的平台管理系统&#xff0c;都存在通过切换左侧的导航栏而使右侧的页面切换的业务需求&#xff1b; 其二、问题描述为&#xff1a; A、步骤一&#…

【Unity】如何在Unity 中创建带有缩放效果的滚动视图(具有吸附效果的实现与优化)?

效果预览&#xff1a; 目录 效果预览&#xff1a; 一、引言&#xff1a; 二、问题描述 三、解决方案&#xff1a; 三、优化&#xff1a; 四、结论 一、引言&#xff1a; 在Unity开发中&#xff0c;经常需要实现滚动视图&#xff08;ScrollView&#xff09;中的内容吸附到…

印象笔记 - Markdown 入门指南

一、Markdown 是什么&#xff1f; Markdown 是一种轻量级的「标记语言」&#xff0c;创始人为约翰格鲁伯&#xff0c;用简洁的语法代替排版&#xff0c;目前被越来越多的知识工作者、写作爱好者、程序员或研究员广泛使用。其常用的标记符号不超过十个&#xff0c;相对于更为复…

一张图读懂人工智能

一、生成人工智能的概念和应用&#xff0c;以及如何使用大型语言模型进行聊天和创造原创内容。这项技术将会对人类和企业产生深远影响。 计算机获得学习、思考和交流的能力&#xff0c;被称为生成人工智能。生成人工智能可以立即获得人类所有知识的总和&#xff0c;并回答任何…

【Intel oneAPI实战】使用英特尔套件解决杂草-农作物检测分类的视觉问题

目录 一、简介&#xff1a;计算机视觉挑战——检测并清除杂草二、基于YOLO的杂草-农作物检测分类2.1、YOLO简介2.2、基于YOLO的杂草-农作物检测分类解决方案 三、基于YOLO的杂草-农作物检测分类系统设计3.1、基于flask框架的demo应用程序后端3.2、基于Vue框架的demo应用程序前端…

c++中使用lambda表达式的作用和用法

lambda表达式&#xff1a; 这是C11引入的一种新特性&#xff0c;它可以让您在需要定义函数对象的地方&#xff0c;直接编写一个匿名的、可以捕获上下文变量的函数体&#xff0c;非常适合用作回调函数、临时计算或定义小型函数对象。 lambda表达式与普通函数类似&#xff0c;也有…

[MYSQL数据库]--mysql的基础知识

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、数据库…

上门废品回收小程序开发,从传统到线上,到“高收益”

随着我国社会经济不断发展&#xff0c;人们的消费水平能力也在不断提升&#xff0c;日常生活中会产生非常多的废弃物品&#xff0c;为废品回收行业带来了大量的机遇。在回收行业的发展前景下&#xff0c;也为年轻人带来了创业机会&#xff0c;既获得利润&#xff0c;也能为社会…

六、OpenAI之嵌入式(Embedding)

嵌入模式 学习怎么将文本转换成数字&#xff0c;解锁搜索等案例。 新的嵌入模型 text-embedding-3-small 和 text-embedding-3-large&#xff0c;是目前最新的并且性能最好的嵌入模型&#xff0c;成本低&#xff0c;支持多语言&#xff0c;拥有控制所有大小的新参数 1. 什么是…

周鸿祎免费课演示AI新品,瞬时流量暴增现场增加服务器

2月29日&#xff0c;360创始人周鸿祎首堂AI免费课开讲&#xff0c;吸引千万网友围观。演讲现场周鸿祎演示了两款AI驱动的新产品。在演示测试版360AI搜索时&#xff0c;由于用户体验火爆&#xff0c;瞬时流量暴增44倍&#xff0c;为满足用户和全网用户需求临时增加了服务器。产品…