Qt系列:调用Edge浏览器示例

news2024/12/26 13:23:17

背景

需要解决以下几个问题

  1. 政府项目新浏览器兼容老系统ActiveX控件,Qt WebEngineView没有直接的实现方案,需要利用Qt的ActiveX兼容模块与浏览器往返多次交互
  2. Qt ActiveX未实现COM事件通知
  3. 官方Win32示例存在滥用lambda函数的嫌疑,lambda函数多层嵌套,程序逻辑层次混乱,整个逻辑被揉成一垛。

官方示例代码

官方介绍文档在这里:https://learn.microsoft.com/microsoft-edge/webview2/get-started/win32。官方代码仓库在这里:GitHub - MicrosoftEdge/WebView2Samples: Microsoft Edge WebView2 samples

摘录一段lambda多层嵌套的代码,你们体会一下:

	CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
		Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
			[hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT {

				// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
				env->CreateCoreWebView2Controller(hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
					[hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
						if (controller != nullptr) {
							webviewController = controller;
							webviewController->get_CoreWebView2(&webview);
						}

						// Add a few settings for the webview
						// The demo step is redundant since the values are the default settings
						wil::com_ptr<ICoreWebView2Settings> settings;
						webview->get_Settings(&settings);
						settings->put_IsScriptEnabled(TRUE);
						settings->put_AreDefaultScriptDialogsEnabled(TRUE);
						settings->put_IsWebMessageEnabled(TRUE);

						// Resize WebView to fit the bounds of the parent window
						RECT bounds;
						GetClientRect(hWnd, &bounds);
						webviewController->put_Bounds(bounds);

						// Schedule an async task to navigate to Bing
						webview->Navigate(L"https://www.bing.com/");

						// <NavigationEvents>
						// Step 4 - Navigation events
						// register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation
						EventRegistrationToken token;
						webview->add_NavigationStarting(Callback<ICoreWebView2NavigationStartingEventHandler>(
							[](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT {
								wil::unique_cotaskmem_string uri;
								args->get_Uri(&uri);
								std::wstring source(uri.get());
								if (source.substr(0, 5) != L"https") {
									args->put_Cancel(true);
								}
								return S_OK;
							}).Get(), &token);
						// </NavigationEvents>

						// <Scripting>
						// Step 5 - Scripting
						// Schedule an async task to add initialization script that freezes the Object object
						webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr);
						// Schedule an async task to get the document URL
						webview->ExecuteScript(L"window.document.URL;", Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
							[](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT {
								LPCWSTR URL = resultObjectAsJson;
								//doSomethingWithURL(URL);
								return S_OK;
							}).Get());
						// </Scripting>

						// <CommunicationHostWeb>
						// Step 6 - Communication between host and web content
						// Set an event handler for the host to return received message back to the web content
						webview->add_WebMessageReceived(Callback<ICoreWebView2WebMessageReceivedEventHandler>(
							[](ICoreWebView2* webview, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT {
								wil::unique_cotaskmem_string message;
								args->TryGetWebMessageAsString(&message);
								// processMessage(&message);
								webview->PostWebMessageAsString(message.get());
								return S_OK;
							}).Get(), &token);

						// Schedule an async task to add initialization script that
						// 1) Add an listener to print message from the host
						// 2) Post document URL to the host
						webview->AddScriptToExecuteOnDocumentCreated(
							L"window.chrome.webview.addEventListener(\'message\', event => alert(event.data));" \
							L"window.chrome.webview.postMessage(window.document.URL);",
							nullptr);
						// </CommunicationHostWeb>

						return S_OK;
					}).Get());
				return S_OK;
			}).Get());

解决方案

下面以实现自动登录外网网关为目标,企业微信自动上线,免开机输入账号密码。这样领导看到你上线,可以开机以后就可以慢慢吃早餐了。(开个玩笑)

本实现方案把官方示例代码做成了静态库,没有添加其它东西。

新建CMake项目

这里用到了Qt5静态库,目的是单文件可执行,不需要部署。需要静态库的读者可以自行删除。代码如下:

cmake_minimum_required(VERSION 3.21)
project(auto-online CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)

add_definitions(/D_UNICODE /DUNICODE)
add_compile_definitions(WIN32 _WINDOWS)

add_compile_options(/utf-8 $<IF:$<CONFIG:Debug>,/MTd,/MT>)
link_directories($ENV{Qt5_DIR}/lib)
link_directories($ENV{Qt5_DIR}/plugins/platforms)
link_directories($ENV{Qt5_DIR}/plugins/imageformats)
link_libraries(UxTheme Winmm Version ws2_32 imm32 dwmapi)

link_libraries($<IF:$<CONFIG:Debug>,Qt5FontDatabaseSupportd,Qt5FontDatabaseSupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5UiToolsd,Qt5UiTools>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5AccessibilitySupportd,Qt5AccessibilitySupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5EventDispatcherSupportd,Qt5EventDispatcherSupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5ThemeSupportd,Qt5ThemeSupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5UiToolsd,Qt5UiTools>)

link_libraries($<IF:$<CONFIG:Debug>,qtpcre2d,qtpcre2>)
link_libraries($<IF:$<CONFIG:Debug>,qtlibpngd,qtlibpng>)
link_libraries($<IF:$<CONFIG:Debug>,qtharfbuzzd,qtharfbuzz>)
link_libraries($<IF:$<CONFIG:Debug>,qtfreetyped,qtfreetype>)
link_libraries($<IF:$<CONFIG:Debug>,qwindowsd,qwindows>)
link_libraries($<IF:$<CONFIG:Debug>,qicnsd,qicns>)
link_libraries($<IF:$<CONFIG:Debug>,qtgad,qtga>)
link_libraries($<IF:$<CONFIG:Debug>,qtiffd,qtiff>)
link_libraries($<IF:$<CONFIG:Debug>,qwbmpd,qwbmp>)
link_libraries($<IF:$<CONFIG:Debug>,qtiffd,qtiff>)
link_libraries($<IF:$<CONFIG:Debug>,qwebpd,qwebp>)
link_libraries($<IF:$<CONFIG:Debug>,qgifd,qgif>)
link_libraries($<IF:$<CONFIG:Debug>,qjpegd,qjpeg>)
link_libraries($<IF:$<CONFIG:Debug>,qicod,qico>)

message("-- Qt5_DIR: " $ENV{Qt5_DIR})
find_package(Qt5 COMPONENTS Core Gui Widgets Network REQUIRED)

include_directories(${CMAKE_SOURCE_DIR}/3rdparty/webview2loader/include)
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/wil/include)
link_directories(${CMAKE_SOURCE_DIR}/3rdparty/webview2loader/lib)
link_libraries(Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network WebView2LoaderStatic)

file(GLOB SRCS *.ui *.cpp *.h)
add_executable(${PROJECT_NAME} WIN32 ${SRCS})
set_directory_properties(PROPERTIES VS_STARTUP_PROJECT ${PROJECT_NAME})
# 环境变量的路径不能带双引号
# message("VCINSTALLDIR: " $ENV{VCINSTALLDIR})
# find_file(VSPATH NAMES "vcruntime140d.dll" PATHS $ENV{VCINSTALLDIR}  REQUIRED NO_DEFAULT_PATH)
# file(TO_NATIVE_PATH ${VSPATH} VSPATH) 
#message("VC CRT PATH: " ${VSPATH})
# set(VSPATH $<IF:$<CONFIG:Debug>,$ENV{VCINSTALLDIR}/Redist/MSVC/14.29.30133/onecore/debug_nonredist/x64/Microsoft.VC142.DebugCRT,$ENV{VCINSTALLDIR}/Redist/MSVC/14.29.30133/x64/Microsoft.VC142.CRT> CACHE STRING "VCRT" FORCE)
# string(CONCAT VSPATH ${VSPATH} ";$ENV{Qt5_DIR}\\bin")

set_target_properties(${PROJECT_NAME} PROPERTIES
    VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    VS_DEBUGGER_ENVIRONMENT "Path=$ENV{Qt5_DIR}\\bin"
    WIN32_EXECUTABLE TRUE)

用Qt Designer设计一个简单的窗口类型

类型名字很简单,就是MainWindow。这个窗口仅启用了布局,没有添加任何控件。UI代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="central_widget">
   <layout class="QVBoxLayout" name="vl1">
    <property name="spacing">
     <number>0</number>
    </property>
    <property name="leftMargin">
     <number>0</number>
    </property>
    <property name="topMargin">
     <number>0</number>
    </property>
    <property name="rightMargin">
     <number>0</number>
    </property>
    <property name="bottomMargin">
     <number>0</number>
    </property>
    <item>
     <widget class="QWidget" name="browser_widget" native="true">
      <layout class="QVBoxLayout" name="vl2"/>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

声明信号槽

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>

#include <windows.h>
#include <stdlib.h>
#include <string>
#include <tchar.h>
#include "wrl.h"
#include "wil/com.h"
#include "WebView2.h"

#define PROFILE_DATA "cw_webview2"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void load_webview2(HWND hWnd);
    void task_run();

    HRESULT cb_create_environment(HWND, HRESULT, ICoreWebView2Environment *);
    HRESULT cb_create_controller(HWND, HRESULT, ICoreWebView2Controller *);
    HRESULT on_navigate_started(ICoreWebView2 *, ICoreWebView2NavigationStartingEventArgs *);
    HRESULT cb_execute_script(HRESULT, LPCWSTR);
    HRESULT on_webmessage_received(ICoreWebView2 *, ICoreWebView2WebMessageReceivedEventArgs *);
    HRESULT on_navigate_completed(ICoreWebView2*, ICoreWebView2NavigationCompletedEventArgs*);
    // HRESULT cb_deliver(HWND, HRESULT, ...);

signals:
    void prepare();
    void load_auth_page();
    void put_name();
    void put_password();
    void click_login();
    void wait(int iminute);

protected:
    void resizeEvent(QResizeEvent* evt) override;
    void closeEvent(QCloseEvent*) override;

protected slots:
    void on_prepare();
    void on_load_auth_page();
    void on_put_name();
    void on_put_password();
    void on_click_login();
    void on_wait(int iminute);

private:
    Ui::MainWindow *ui;
    // Pointer to WebViewController
    wil::com_ptr<ICoreWebView2Controller> m_controller;
    bool m_brunning = false;
    QThread* m_task;

    // Pointer to WebView window
    wil::com_ptr<ICoreWebView2_15> m_webview;
    Microsoft::WRL::ComPtr<ICoreWebView2ExecuteScriptCompletedHandler> js_cb;
    Microsoft::WRL::ComPtr<ICoreWebView2WebMessageReceivedEventHandler> msg_cb;
};

#endif // MAINWINDOW_H

在合适的时机填入账号密码

本来想做成弹框让用户输入账号密码,忽然发现这种做法对信息部门的同事不太友好,各位读者如有请自行修改。示例代码中的网关地址、用户名和密码是乱写的。

#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QResizeEvent>
#include <QRect>
#include <QDebug>
#include <QStandardPaths>
#include <QDir>
#include <QMessageBox>
#include <QTimer>
#include <QApplication>

using namespace Microsoft::WRL;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    load_webview2((HWND)ui->browser_widget->winId());
    setWindowTitle(u8"自动连接外网工具");
    connect(this, SIGNAL(prepare()), this, SLOT(on_prepare()), Qt::QueuedConnection);
    connect(this, SIGNAL(load_auth_page()), this, SLOT(on_load_auth_page()), Qt::QueuedConnection);
    connect(this, SIGNAL(put_name()), this, SLOT(on_put_name()), Qt::QueuedConnection);
    connect(this, SIGNAL(put_password()), this, SLOT(on_put_password()), Qt::QueuedConnection);
    connect(this, SIGNAL(click_login()), this, SLOT(on_click_login()), Qt::QueuedConnection);
    connect(this, SIGNAL(wait(int)), this, SLOT(on_wait(int)), Qt::QueuedConnection);
}

MainWindow::~MainWindow()
{
    delete ui;
}

HRESULT MainWindow::on_navigate_started(ICoreWebView2 *webview,
                                        ICoreWebView2NavigationStartingEventArgs *args)
{
    wil::unique_cotaskmem_string uri;
    args->get_Uri(&uri);
    QString zurl = QString::fromStdWString(uri.get());
    // std::wstring source(uri.get());
    // if (source.substr(0, 5) != L"https")
    // {
    //     args->put_Cancel(true);
    // }
    qInfo().noquote() << "navigate: " << zurl;
    return S_OK;
}


HRESULT MainWindow::cb_execute_script(HRESULT errorCode, LPCWSTR resultObjectAsJson)
{
    LPCWSTR URL = resultObjectAsJson;
    //doSomethingWithURL(URL);
    qInfo() << "executed javascript.";
    return S_OK;
}

HRESULT MainWindow::on_webmessage_received(ICoreWebView2 *webview,
                                           ICoreWebView2WebMessageReceivedEventArgs *args)
{
    wil::unique_cotaskmem_string message;
    args->TryGetWebMessageAsString(&message);
    // processMessage(&message);
    //webview->PostWebMessageAsString(message.get());
    QString zmsg = QString::fromStdWString(message.get());
    qInfo().noquote() << "message: " << zmsg;
    return S_OK;
}

HRESULT MainWindow::on_navigate_completed(ICoreWebView2 *webview,
                                          ICoreWebView2NavigationCompletedEventArgs *args)
{
    BOOL bsuccess;
    COREWEBVIEW2_WEB_ERROR_STATUS ierror;
    args->get_IsSuccess(&bsuccess);
    args->get_WebErrorStatus(&ierror);
    LPWSTR lpuri;
    webview->get_Source(&lpuri);
    QString zuri = QString::fromStdWString(lpuri);
    qInfo().noquote() << "complate : " << bsuccess << ", " << ierror << ", " << zuri;
    return S_OK;
}

void MainWindow::resizeEvent(QResizeEvent *evt)
{
    if (!m_controller)
        return;
    RECT rc;
    QRect qrc =  ui->browser_widget->rect();
    rc.left = qrc.left();
    rc.top = qrc.top();
    rc.right = qrc.right();
    rc.bottom = qrc.bottom();
    m_controller->put_Bounds(rc);
}

void MainWindow::closeEvent(QCloseEvent *)
{
    m_brunning = false;
    m_task->terminate();
    QThread::sleep(1);
    m_task->deleteLater();
    m_task = nullptr;
}

void MainWindow::on_prepare()
{
    if (!m_webview)
        return;
    m_webview->NavigateToString(L"Preparing...");
}

void MainWindow::on_load_auth_page()
{
    if (!m_webview)
        return;
    m_webview->Navigate(L"http://1.1.1.3/ac_portal/default/pc.html?tabs=pwd");
    qInfo() << "begin load auth page";
}

void MainWindow::on_put_name()
{
    if (!m_webview)
        return;
    m_webview->ExecuteScript(L"$('#password_name').val('test1');"
                             "window.chrome.webview.postMessage('put name ok.');",
                             js_cb.Get());
}

void MainWindow::on_put_password()
{
    if (!m_webview)
        return;
    m_webview->ExecuteScript(L"$('#password_pwd').val('123456');"
                             "window.chrome.webview.postMessage('put password ok.');",
                             js_cb.Get());
}

void MainWindow::on_click_login()
{
    if (!m_webview)
        return;
    m_webview->ExecuteScript(L"$('#password_submitBtn').click();"
                             "window.chrome.webview.postMessage('click login ok.');",
                             js_cb.Get());
}

void MainWindow::on_wait(int iminute)
{
    QString zhtml = QString("Already wairted %1 minutes.").arg(iminute);
    if (!m_webview)
        return;
    m_webview->NavigateToString(zhtml.toStdWString().c_str());
}

HRESULT MainWindow::cb_create_controller(HWND hWnd, HRESULT result,
                                         ICoreWebView2Controller *controller)
{
    if (!controller)
        return E_POINTER;

    m_controller = controller;
    wil::com_ptr<ICoreWebView2> webview;
    HRESULT ir =  m_controller->get_CoreWebView2(&webview);
    if (FAILED(ir))
        return ir;
    ir = webview->QueryInterface(IID_ICoreWebView2_15, (void **)&m_webview);
    if (FAILED(ir))
        return ir;

    // Add a few settings for the webview
    // The demo step is redundant since the values are the default settings
    wil::com_ptr<ICoreWebView2Settings> settings;
    m_webview->get_Settings(&settings);
    settings->put_IsScriptEnabled(TRUE);
    settings->put_AreDefaultScriptDialogsEnabled(TRUE);
    settings->put_IsWebMessageEnabled(TRUE);

    // Resize WebView to fit the bounds of the parent window
    RECT bounds;
    GetClientRect(hWnd, &bounds);
    m_controller->put_Bounds(bounds);

    m_webview->NavigateToString(L"Preparing...");

    // <NavigationEvents>
    // Step 4 - Navigation events
    // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation
    EventRegistrationToken token;
    auto nav_func = std::bind(&MainWindow::on_navigate_started, this, std::placeholders::_1,
                              std::placeholders::_2);
    auto nav_cb = Callback<ICoreWebView2NavigationStartingEventHandler>(nav_func);
    m_webview->add_NavigationStarting(nav_cb.Get(), &token);

    auto com_func = std::bind(&MainWindow::on_navigate_completed, this, std::placeholders::_1,
                              std::placeholders::_2);
    auto com_cb = Callback<ICoreWebView2NavigationCompletedEventHandler>(com_func);
    m_webview->add_NavigationCompleted(com_cb.Get(), &token);
    // </NavigationEvents>

    // <Scripting>
    // Step 5 - Scripting
    // Schedule an async task to add initialization script that freezes the Object object
    // 注入脚本
    //webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr);
    // Schedule an async task to get the document URL
    auto js_func = std::bind(&MainWindow::cb_execute_script, this, std::placeholders::_1,
                             std::placeholders::_2);
    js_cb = Callback<ICoreWebView2ExecuteScriptCompletedHandler>(js_func);
    //webview->ExecuteScript(L"window.document.URL;", js_cb.Get());
    // </Scripting>

    // <CommunicationHostWeb>
    // Step 6 - Communication between host and web content
    // Set an event handler for the host to return received message back to the web content
    auto msg_func = std::bind(&MainWindow::on_webmessage_received, this, std::placeholders::_1,
                              std::placeholders::_2);
    msg_cb = Callback<ICoreWebView2WebMessageReceivedEventHandler>(msg_func);
    m_webview->add_WebMessageReceived(msg_cb.Get(), &token);
    // Schedule an async task to add initialization script that
    // 1) Add an listener to print message from the host
    // 2) Post document URL to the host
    // 注入脚本
    //webview->AddScriptToExecuteOnDocumentCreated(
    //    L"window.chrome.webview.addEventListener(\'message\', event => alert(event.data));" \
    //    L"window.chrome.webview.postMessage(window.document.URL);",
    //    nullptr);
    // </CommunicationHostWeb>
    m_task = QThread::create(&MainWindow::task_run, this);
    m_task->setParent(this);
    m_task->start();
    return S_OK;
}

void MainWindow::load_webview2(HWND hWnd)
{
    MainWindow *obj = this;
    auto func = &MainWindow::cb_create_environment;
    auto fn = [hWnd, obj, func](HRESULT result, ICoreWebView2Environment * env)->HRESULT
    {
        return (obj->*func)(hWnd, result, env);
    };
    auto cb = Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(fn);
    QDir d(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
    auto ztemp = d.absoluteFilePath(PROFILE_DATA).toStdWString();
    HRESULT ir = CreateCoreWebView2EnvironmentWithOptions(nullptr, ztemp.c_str(), nullptr, cb.Get());
    if (FAILED(ir))
    {
        qCritical() << "create webview2 failed. code: " << ir;
        QTimer::singleShot(3000, []()
        {
            QApplication::quit();
            _Exit(1);
        });
        QMessageBox::warning(this,
                             windowTitle(),
                             u8"严重错误。程序即将退出。\n"
                             "请检查Microsoft Edge WebView2 Runtime是否存在。\n");
    }
}

void MainWindow::task_run()
{
    if (!m_controller || !m_webview)
        return;
    m_brunning = true;
    while (m_brunning)
    {
        emit on_prepare();
        QThread::sleep(3);
        if (!m_brunning)
            break;
        emit load_auth_page();
        QThread::sleep(10);
        if (!m_brunning)
            break;
        emit put_name();
        QThread::sleep(3);
        if (!m_brunning)
            break;
        emit put_password();
        QThread::sleep(3);
        if (!m_brunning)
            break;
        emit click_login();
        for (int i = 0; i < 10; i++)
        {
            QThread::sleep(60);
            if (!m_brunning)
                break;
            emit wait(i + 1);
        }
    }
}

HRESULT MainWindow::cb_create_environment(HWND hWnd, HRESULT result, ICoreWebView2Environment *env)
{
    MainWindow *obj = this;
    auto func = &MainWindow::cb_create_controller;
    auto fn = [hWnd, obj, func](HRESULT result, ICoreWebView2Controller * controller)->HRESULT
    {
        return (obj->*func)(hWnd, result, controller);
    };
    // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
    auto cb = Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(fn);
    HRESULT hr = env->CreateCoreWebView2Controller(hWnd, cb.Get());
    return hr;
}

免责声明

此代码仅供技术研究娱乐之用,禁止用于商业用途,否则一切后果自负。

作者:岬淢箫声

岬淢箫声的博客_CSDN博客-C/C++,MFC/VC,桌面H5领域博主icon-default.png?t=N176https://caowei.blog.csdn.net/

转载请注明来源

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年保健饮品行业分析:市场规模不断攀升,年度销额增长近140%

随着人们健康意识的不断增强&#xff0c;我国保健品市场需求持续增长&#xff0c;同时&#xff0c;保健饮品的市场规模也在不断攀升。 根据鲸参谋电商数据显示&#xff0c;2022年度&#xff0c;京东平台上保健饮品的年度销量超60万件&#xff0c;同比增长了约124%&#xff1b;该…

flutter-第1章-配置环境

flutter-第1章-配置环境 本文针对Windows系统。 一、安装Android Studio 从Android Studio官网下载最新版本&#xff0c;一直默认安装就行。 安装完成要下载SDK&#xff0c;可能会需要科学上网。 打开AS&#xff0c;随便创建一个新项目。 点击右上角的SDK Manager 找到SDK…

c#: NetTopologySuite凹凸多边形计算

环境&#xff1a; .net 6.0NetTopologySuite 2.5.0vs2022平面二维 一、夹角计算 1.1 计算向量与x轴正方向的夹角 方法: AngleUtility.Angle(Coordinate p) 下图上的t2即为p&#xff0c;之所以这么写是为了和AngleUtility.AngleBetweenOriented做比较 注意&#xff1a; 结果…

MySQL 服务正在启动.MySQL 服务无法启动.服务没有报告任何错误。请键入 NET HELPMSG 3534 以获得更多的帮助。总结较全 (已解决)

输入以下命令启动mysql&#xff1a; net start mysql出现以下错误提示&#xff1a; MySQL 服务正在启动 .MySQL 服务无法启动。服务没有报告任何错误。请键入 NET HELPMSG 3534 以获得更多的帮助。 出现这个问题的话&#xff0c;一般有几个情况&#xff1a; 一、MySQL安装文…

面试7分看能力,3分靠嘴皮,剩下90分就靠这份Java面试八股文

有句话说的好&#xff0c;面试中7分靠能力&#xff0c;3分靠嘴皮刚开始面试的时候&#xff0c;介绍项目一般都是凸显我们技能的重中之重&#xff0c;它对一次面试的成败可以说具有决定性作用&#xff0c;这就涉及到我们的表达能力了&#xff0c;有人说我其实水平都在线&#xf…

大学生开学买什么,返校必备数码好物推荐

开学还不知道需要准备些什么&#xff0c;这篇开学数码好物&#xff0c;希望能够对你在开学购买的好物有一些帮助&#xff0c;一款好的数码装备&#xff0c;可以让我们在学校学习当中能够用最少的时间&#xff0c;最大的产出&#xff0c;节省时间&#xff0c;提高学习效率&#…

2023-02-17 学习记录--TS-邂逅TS(一)

TS-邂逅TS&#xff08;一&#xff09; 不积跬步&#xff0c;无以至千里&#xff1b;不积小流&#xff0c;无以成江海。&#x1f4aa;&#x1f3fb; 一、TypeScript在线编译器 https://www.typescriptlang.org/play/ 二、类型 1、普通类型 number&#xff08;数值型&#xff…

零信任-360连接云介绍(9)

​360零信任介绍 360零信任又称360连接云安全访问平台(下文简称为&#xff1a;360连接云)&#xff0c;360连接云&#xff0c;是360基于零信任安全理念&#xff0c;以身份为基础、动态访问控制为核心打造的安全访问平台。 通过收缩业务暴露面、自适应增强身份认证、终端持续检…

操作系统(day11)--快表,两级页表

具有快表的地址变换机构 时间局限性&#xff1a;会有大量连续的指令需要访问同一个内存块的数据的情况&#xff08;程序中的循环&#xff09; 空间局限性&#xff1a;一旦程序访问了某个存储单元&#xff0c;在不久之后&#xff0c;其附近的存储单元也很有可能被访问。&#xf…

MySQL(一)服务器连接 库的基本操作

目录 一、连接服务器 二、简单使用 三、校验规则 条件筛选 where 进行order排序 三、查看数据库 使用 show databases&#xff1b;&#xff08;注意分号和最后一个s&#xff09; 显示创建数据库的详情信息&#xff1a;使用show create database test2; 四、修改数据库 五…

干货 | 有哪些安慰剂按钮的设计?

仔细观察我们的生活&#xff0c;你会发现处处都是安慰剂按钮&#xff0c;ATM的点钞声、开启空调的呼呼声&#xff0c;这些都对用户心里产生了有意的引导作用&#xff0c;当你打开了空调按钮&#xff0c;先播放声音会让你感觉你按下的按钮起到了作用。 我们的大脑不喜欢杂乱无章…

Markdown简单语法总结

前言在好多博客的编写中会遇到Markdown标记语言&#xff0c;所以学习了一下相关语法。之前虽然也了解过&#xff0c;但是并不深刻。在这里进行一个简单的总结。1、标题标题可以使用大标题和中标题、一到六级标题两种不同方式。对于大标题&#xff0c;直接在标题文字下方输入即可…

HTML中嵌入B站视频

HTML中嵌入B站视频 在网页中实现一个HTML播放器需要先从b站获取视频嵌入代码, 以前嵌入代码可以从视频分享那里拿到, 现在好像不行了 必须是自己投稿的视频, 从投稿管理页面才能找到 复制嵌入代码 建一个.html文件, 放入下面代码 <!DOCTYPE html> <html><head…

SpringCloud - 入门

目录 服务架构演变 单体架构 分布式架构 分布式架构要考虑的问题 微服务 初步认识 案例Demo 服务拆分注意事项 服务拆分示例 服务调用 服务架构演变 单体架构 将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署优点&#xff1a; 架构简单部署成本低缺…

论文解读 | [CVPR2020] ContourNet:向精确的任意形状场景文本检测迈出进一步

目录 1 研究背景和目的 1.1 主要贡献&#xff1a; 1.2 两个挑战&#xff1a; 2 ContourNet 3 方法论 3.1 Adaptive-RPN 3.2 LOTM 3.3 点重定位算法 4 实验和结果 论文地址&#xff1a;ContourNet: Taking a Further Step toward Accurate Arbitrary-shaped Scene Tex…

第八章 - 数据分组( group by , having , select语句顺序)

第八章 - 数据分组 group by数据分组过滤分组 having分组排序groub by语句的一些规定select语句顺序数据分组 在使用group by进行分组时&#xff0c;一般都会配合聚合函数一起使用&#xff0c;实现统计数据的功能。比如下面例子&#xff0c;需要按性别计算人数。按性别进行分组…

内嵌于球的等边三棱柱

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 做一个网络让输入只有3个节点&#xff0c;每个训练集里有两张图片&#xff0c;让B的训练集全为0&#xff0c;排列组合A&#xff0c;观察迭代次数平均值的变化。共完成了64组&#xff0c;但只有12组不同的迭代次数。 差值结构 A-B 迭代次…

【银河麒麟V10操作系统】修改屏幕分辨率的方法

文章目录前言系统概述方法1&#xff1a;使用命令行修改方法2&#xff1a;写文件修改方法3&#xff1a;界面端修改的方法前言 本文记录了银河麒麟V10系统修改分辨率的方法。 使用命令行修改写文件修改界面端修改的方法 系统概述 方法1&#xff1a;使用命令行修改 打开终端&am…

零基础学软件测试可行么

当然是可行的&#xff0c;软件测试是一门对于零基础小白非常友好的一门语言~不少小伙伴在选择IT行业的时候都会从软件测试入手。在开始学习之前&#xff0c;首先需要了解软件测试到底是什么&#xff0c;包括哪些知识点&#xff0c;最后的才是找一个靠谱的学习路线去学习。 其实…

用Python实现一个电影订票系统

一、效果展示通过Python实现一个电影订票系统&#xff0c;效果如下所示&#xff1a;二、整体结构图三、代码分解3.1 infos.py一部电影的详细信息适合用 字典 结构来存储&#xff0c;我们可以给字典里添加多个键值对来保存电影的名称、座位表和宣传时用的字符画&#xff0c;比如…