WebView2教程(基于C++)【一】环境初始化
创建一个VisualStudio C++项目,通过NuGet包管理器安装两个包:
注意,在项目属性页设置项目使用:C++ 20,子系统设置成窗口(相应的预处理器也要改变),DPI识别设置成每个监视器高DPI识别。
附加依赖项设置以下几项:
dwmapi.lib
shell32.lib
comctl32.lib
usp10.lib
kernel32.lib
user32.lib
新建一个main.cpp代码如下:
#include <Windows.h>
#include "App.h"int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine, _In_ int nCmdShow)
{auto result = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);if (result != S_OK) {return 0;}App::init();MSG msg;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}CoUninitialize();return 0;
}
这是入口方法,我们在入口方法里初始化了App类
下面是App类的头文件代码如下:
#pragma once
#include <Windows.h>
#include <fstream>
#include <filesystem>
#include <wrl.h>
#include <wil/com.h>
#include <WebView2.h>
#include <Shlobj.h>
#include <shellapi.h>class App
{
public:~App();static void init();static void dispose();static App* get();static ICoreWebView2Environment* getWebViewEnv();static std::wstring getAppPath();
private:App();void initConfig();void regScheme();bool checkRuntime();bool checkRegKey(const HKEY& key, const std::wstring& subKey);bool ensureAppFolder();HRESULT envCallBack(HRESULT result, ICoreWebView2Environment* env);
};
先看来看看这个类的一部分代码(不是全部):
#include "App.h"
#include <rapidjson/document.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <fstream>
#include "Util.h"
#include <string>
#include <vector>
#include <WebView2EnvironmentOptions.h>
#include "Win.h"using namespace Microsoft::WRL;namespace {static App* app;static rapidjson::Document d;static std::vector<Win*> wins;static std::filesystem::path appPath;static ICoreWebView2Environment* webViewEnv;
}App::App()
{initConfig();if (!checkRuntime()) {return;}if (!ensureAppFolder()) {return;}regScheme();auto envCBInstance = Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(this, &App::envCallBack);HRESULT result = CreateCoreWebView2EnvironmentWithOptions(nullptr, appPath.c_str(), nullptr/*options.Get()*/, envCBInstance.Get());if (FAILED(result)) {return;}
}
App::~App()
{for (size_t i = 0; i < wins.size(); i++){delete wins[i];}
}
void App::init() {if (app) {return;}app = new App();
}
App* App::get() {return app;
}
void App::dispose()
{delete app;
}
App::init();执行之后,就创建了一个App对象,这个对象被保存在静态变量app中,在App的构造函数中,先初始化了应用程序的配置信息。代码如下:
void App::initConfig()
{std::ifstream file("config.json");std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());d.Parse(content.c_str());
}
这段代码读取应用程序(exe文件)所在目录下的config.json文件,并把这个json文件存储在静态变量:static rapidjson::Document d;中,以后我们会从这个d中获取配置信息。
这个config.json文件的代码如下:
{"appName": "WebView2JS","windows": [{"id": "FirstWindow","w": 1200,"h": 800,"miniWidth": 1200,"miniHeight": 800,"resizable": true,"title": "WebView2JS窗口","frame": false,"shadow": true,"position": {"x": 100,"y": 100,"centerOfScreen": true},"webviews": [{"id": "FirstWebView","url": "https://wv2js/index.html","disableWindowOpen": null,"area": {"left": 0,"right": 0,"top": 0,"bottom": 0,"width": -1,"height": -1}}]}]
}
这里配置了窗口和webview的一些基本信息。
需要注意的是,我们读取JSON用到了RapidJSON,至于怎么用这个库,我们这里就不多做介绍了。甚至你可以完全不用json配置文件。
initConfig之后,就会执行checkRuntime方法,代码如下:
bool App::checkRuntime()
{std::wstring regSubKey = L"\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}";bool hasRuntime = checkRegKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\WOW6432Node" + regSubKey);if (hasRuntime) return true;hasRuntime = checkRegKey(HKEY_CURRENT_USER, L"Software" + regSubKey);if (!hasRuntime) {auto result = MessageBox(nullptr, L"error", L"error", MB_OKCANCEL | MB_ICONINFORMATION | MB_DEFBUTTON1);if (result == IDOK) {ShellExecute(0, 0, L"https://go.microsoft.com/fwlink/p/?LinkId=2124703", 0, 0, SW_SHOW);}return false;}return true;
}
这个方法会判断当前的用户环境,是否安装了WebView2的运行时,如果没有,则打开一个网页,让用户去下载WebView2的运行时。
接下去执行ensureAppFolder方法,代码如下:
bool App::ensureAppFolder() { PWSTR pathTmp;auto ret = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pathTmp);if (ret != S_OK) {CoTaskMemFree(pathTmp);auto result = MessageBox(nullptr, L"error", L"error", MB_OK | MB_ICONINFORMATION | MB_DEFBUTTON1);exit(1);return false;}appPath = pathTmp;CoTaskMemFree(pathTmp);appPath /= convertToWideChar(d["appName"].GetString());if (!std::filesystem::exists(appPath)) {auto flag = std::filesystem::create_directory(appPath);if (!flag) {MessageBox(nullptr, L"error", L"error", MB_OK | MB_ICONINFORMATION | MB_DEFBUTTON1);exit(1);return false;}}return true;
}
这个方法会初始化一个应用程序缓存目录,:C:Users[user name]AppDataRoamingWebView2JS,其中WebView2JS是从配置文件中读取的。
目录中的文件如下图所示,这与Electron应用差不多
这个路径被保存到appPath静态变量中了。
接下去会执行regScheme方法:
void App::regScheme()
{auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();options->put_AdditionalBrowserArguments(L"--allow-file-access-from-files");Microsoft::WRL::ComPtr<ICoreWebView2EnvironmentOptions4> options4;HRESULT oeResult = options.As(&options4);const WCHAR* allowed_origins[1] = { L"*" };auto defaultRegistration = Microsoft::WRL::Make<CoreWebView2CustomSchemeRegistration>(L"wv2js");defaultRegistration->put_HasAuthorityComponent(TRUE);defaultRegistration->put_TreatAsSecure(TRUE);defaultRegistration->SetAllowedOrigins(1, allowed_origins);ICoreWebView2CustomSchemeRegistration* registrations[1] = { defaultRegistration.Get() };options4->SetCustomSchemeRegistrations(1, static_cast<ICoreWebView2CustomSchemeRegistration**>(registrations));
}
这个方法会为WebView注册一个自定义协议,这样我们就可以用:https://wv2js这个域名加载我们的自定义页面了。
App类构造函数中最后几行代码以异步的方式创建WebView2的环境变量对象,异步回调方法为:envCallBack,这个方法的代码如下:
HRESULT App::envCallBack(HRESULT result, ICoreWebView2Environment* env)
{webViewEnv = env;rapidjson::Value& winConfigs = d["windows"].GetArray();for (size_t i = 0; i < winConfigs.Size(); i++){wins.push_back(new Win(winConfigs[i]));}return S_OK;
}
在这个方法中,webview2的环境对象被保存到静态变量webViewEnv中了,接着创建了窗口对象,并保存到一个容器wins中(静态变量)。
如你所见,依据我们的配置文件,我们是可以在应用程序启动时,直接创建多个窗口的。
App类还有几个简单的方法,如下所示:
ICoreWebView2Environment* App::getWebViewEnv()
{return webViewEnv;
}std::wstring App::getAppPath()
{return appPath.wstring();
}
这两个方法用于给其他类提供全局信息。
相关文章:

WebView2教程(基于C++)【一】环境初始化
创建一个VisualStudio C项目,通过NuGet包管理器安装两个包: 注意,在项目属性页设置项目使用:C 20,子系统设置成窗口(相应的预处理器也要改变),DPI识别设置成每个监视器高DPI识别。 …...
go语言中context的用法
0 概述 Context 是 Go 语言中非常重要的一个概念,它主要用于跨多个函数或 goroutine 传递 取消信号、超时控制、截止时间 和 请求范围数据。在并发编程中,Context 提供了更好的控制和管理,尤其是当你需要在多个 goroutine 之间传递状态或进行…...
概括网络给社会生活带来的种种影响
题目 【2002年国考申论】给定资料反映了网络给社会生活带来的种种影响,用不超过200字对这些影响进行概括。 要求:全面,有条理,有层次。(20分) 审题 特定事实:网络给社会生活带来的种种影响基本题型:单一…...
OpenCV相机标定与3D重建(16)将点从齐次坐标转换为非齐次坐标函数convertPointsFromHomogeneous()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::convertPointsFromHomogeneous 是 OpenCV 库中的一个函数,用于将点从齐次坐标(homogeneous coordinates)…...

Jmeter如何对UDP协议进行测试?
Jmeter如何对UDP协议进行测试? 1 jmeter-plugins安装2 UDP-Protocol Support安装3 UDP协议测试 1 jmeter-plugins安装 jmeter-plugins是Jmeter的插件管理器;可以组织和管理Jmeter的所有插件;直接进入到如下页面,选择如图的选项进…...

Unix 传奇 | 谁写了 Linux | Unix birthmark
注:本文为 “左耳听风”陈皓的 unix 相关文章合辑。 皓侠已走远,文章有点“年头”,但值得一阅。 文中部分超链已沉寂。 Unix 传奇 (上篇) 2010 年 04 月 09 日 陈皓 了解过去,我们才能知其然,更知所以然。总结过去…...

【网络】传输层协议UDP/TCP网络层IP数据链路层MACNAT详解
主页:醋溜马桶圈-CSDN博客 专栏:计算机网络原理_醋溜马桶圈的博客-CSDN博客 gitee:mnxcc (mnxcc) - Gitee.com 目录 1.传输层协议 UDP 1.1 传输层 1.2 端口号 1.3 UDP 协议 1.3.1 UDP 协议端格式 1.3.2 UDP 的特点 1.3.3 面向数据报 1…...

RTMP推流平台EasyDSS在无人机推流直播安防监控中的创新应用
无人机与低空经济的关系密切,并且正在快速发展。2024年中国低空经济行业市场规模达到5800亿元,其中低空制造产业占整个低空经济产业的88%。预计未来五年复合增速将达到16.03%。 随着科技的飞速发展,公共安防关乎每一个市民的生命财产安全。在…...

ORACLE逗号分隔的字符串字段,关联表查询
使用场景如下: oracle12 以前的写法: selectt.pro_ids,wm_concat(t1.name) pro_names from info t,product t1 where instr(,||t.pro_ids|| ,,,|| t1.id|| ,) > 0 group by pro_ids oracle12 以后的写法: selectt.pro_ids,listagg(DIS…...

1.5 多媒体系统简介
目录 多媒体系统声音图形与图像动画和视频 多媒体系统 多媒体可分为感觉媒体、表示媒体、表现媒体、交换媒体。 感觉媒体:直接使人产生感觉的媒体,比如声音、图像、视频。表示媒体:计算机中记录感觉的数据格式。表现媒体:记录感觉…...

数据分析学习Day1-使用matplotlib生成2小时每分钟的气温可视化分析图
注意:需要提前下载matplotlib包 pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple import matplotlib.pyplot as plt import random from matplotlib import font_manager # 数据准备 x list(range(121)) # 使用 list() 转换为列表 y [rando…...

ansible部署nginx:1个简单的playbook脚本
文章目录 hosts--ventoryroles执行命令 使用ansible向3台centos7服务器上安装nginx hosts–ventory [rootstand playhook1]# cat /root/HOSTS # /root/HOSTS [webservers] 192.168.196.111 ansible_ssh_passpassword 192.168.196.112 ansible_ssh_passpassword 192.168.196.1…...

三、汇总统计
1.SUM、COUNT、AVERAGE 注意:count函数是计算区域中包含数字的单元格的个数,以上案例中两个空白单元格和一个中文列标题都是没有计算在内的。 平均函数AVERAGE也是按照17进行求平均值的。所以在使用平均值的函数时候,可以根据实际情况看是…...
opencv实现给图像加上logo图像
要用Python和OpenCV给图片加上logo,可以按照以下步骤实现: 读取logo和image图片。 调整logo的大小以适应image。 将logo放置在image的指定位置。 将logo和image合并。 以下是实现代码: import cv2# 读取logo和image图片 logo cv2.imre…...

亚马逊云科技2024 re:Invent大会亮点:Nova大模型与AI基础设施全面升级
引言 作为云计算领域的年度盛会,亚马逊云科技(AWS)的re:Invent大会一直是业界瞩目的焦点。2024年的大会不负众望,推出了一系列重磅产品和服务,尤其是在人工智能和大模型方面的创新令人印象深刻。本文将为您深入解析此次大会的主要亮点,探讨AWS在AI时代的最新布局及其对行业的潜…...
总结与提升
今天学习了ai,对今天学习的内容进行总结。 本文参考chat gpt-4的训练文献。 模型架构基础 Transformer 架构:ChatGPT 采用了 Transformer 架构,这是一种基于自注意力机制的深度学习模型架构。它能够并行计算文本中的长期依赖关系ÿ…...

入门pytorch-Transformer
前言 虽然Transformer是2017年由Google推出,如果按照读论文只读近两年的思路看,那它无疑是过时的,但可惜的是,目前很多论文的核心依然是Transformer,或者由其进行改进的,故本文使用pytorch来搭建一下Trans…...

泛型编程--
auto自动推导数据类型 函数模板 定义和调用 函数模板具体化 函数模板通用版本之外的一个特殊版本 函数模板 具体化函数 ,它们的声明和定义都可以分开写。 声明 定义 函数模板写变量 模板参数缺省 类成员函数作为函数模板 类构造函数是函数模板 函数模板重载 函数模…...

【大语言模型】LangChain 核心模块介绍(Agents)
【大语言模型】LangChain 核心模块介绍(Agents) 一、简介二、Agents 的核心概念三、实战案例3.1、需求说明3.2、实现思路3.3、完整源码 一、简介 我们都知道四肢的绝大部分动作都是由大脑决定的,大脑通过中枢神经下发自己的操作指令…...

19C-RAC 环境mgmtca.trc.1过大
客户监控告警/u01使用率超过80%,通过一层层目录查看,发现是mgmtca.trc.1过大导致的告警 [rootgsdb1 ~]# du -sh /u01/app/grid/cfgtoollogs/mgmtca/mgmtca.trc.1 103G /u01/app/grid/cfgtoollogs/mgmtca/mgmtca.trc.1 查看MOS文档:Huge …...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...