Windows 程序开机自启动速度优化,为什么腾讯会议自启动速度那么高?

目录

一、问题的说明和定义

二、问题的分析

1.问题初步分析

2.详细的分析:

2.1Windows常见的自启动方式

2.2Windows常见的自启动方式的细节分析

三、问题的解决方案

1、为什么腾讯会议Rooms那么快

2.我们是否可以跟腾讯会议一样快


一、问题的说明和定义

这两天有个优化项需要做个技术调研,就是我们项目的开机启动时间比较长,大概20多秒,但对比腾讯会议Rooms,这款软件的自启动就异常的快,用户登录几秒就可以看到程序界面。

拿到任务了,开干吧!!!!!!

二、问题的分析

1.问题初步分析

首先分析猜测慢的原因:

  • 从双击程序图标启动那一刻,到看到界面的过程比较耗时,这个做了验证,在4秒左右(这个可有具体负责的工程师单独优化),相比于二十多秒的启动时间,还是有很多其他因素需要优化的。
  • 操作系统的开机自启动有延迟,本文主要分析的是这种场景的耗时点,以及可能的解决方案。

2.详细的分析:

2.1Windows常见的自启动方式

知己知彼百战百胜,所以第一步,先学习了解Windows系统中自启动的方案都有哪些,常见的方式四个

  1. 指定文件夹中放入程序快捷方式,或者启动脚本bat文件。
  2. 注册表自启动方式
  3. 任务计划程序方式
  4. Windows任务自启动

针对上面四种的详细描述,网上一搜一大把,我这里就不在赘述了,但我想把我在分析过程中,针对这几种方案的差别,做个简单的描述

2.2Windows常见的自启动方式的细节分析


针对第一、二种,没啥好说的,个人感觉,私人电脑自己配置可以这么玩,简单粗暴,但针对一款应用软件的功能,这么做就不是很合适了,不推荐


第三种自启动方式,一共有五个注册表类型可以实现自启动,如下:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run

表示任何账户每一次登陆到Windows系统都会自动启动在这个项下面注册的程序

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce

表示任何账户下一次登陆到Windows系统会自动启动在这个项下面注册的程序,以后就不会自启了

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run

表示当前账户每一次登陆到Windows系统都会自动启动在这个项下面注册的程序

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce

表示当前账户下一次登陆到Windows系统会自动启动在这个项下面注册的程序,以后就不会自启了

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run

表示程序需要已管理员权限启动

自启动耗时在哪里呢?

1、注册表自启动的程序是在用户登录后,是否可以提到用户登录前呢?这样就可以提升启动速度了。针对Win11来说,是可以的,win11有一个控制选项,设置里面,找账户

“使用我的登录信息在更新后自动完成设置” 该选项保持勾选,即可在用户登录前启动

win10应该没有该选项控制

2、windows操作系统有一个注册表的值StartupDelayInMS,可以控制在用户登录后,延迟若干秒后,在自启动应用程序,该机制主要是为了把计算机资源留给操作系统用,该字段默认是没有的,没有的情况下,操作系统的默认延时未10s,可以加上该字段,然后设置其为0,即可取消这10s的延时HKEY_USERS\S-1-5-21-1084762125-4268433165-138902895-1001\Software\Microsoft\Windows\CurrentVersion\Explorer\Serialize

Serialize项如果没有,需要添加一下,然后再该项下,新增一个DWORD的值StartupDelayInMS,设置16进制为0即可

3、如果自启动项比较多的时候,启动顺序是不可控的,这样肯定会有些排在后面,导致启动比较慢,该问题的解决方案会在后面说


第四种方式就是通过服务启动

网上制作服务的博客很多,不在赘述

windows 把exe 设置成服务运行_exe做成服务_小小的技术员的博客-CSDN博客

该方案的优点是,在计算机启动后,服务会优先启动,无论是否用户登录,缺点是从xp或者win7以后,不支持UI界面的服务,如果将UI程序制作为服务,那么UI部分也是不会启动的,原因可参看[笔记]Windows安全之《二》Session0隔离及相关启动技术_会话隔离_二进制怪兽的博客-CSDN博客

下面是启动指定app的服务程序,可参考

#ifndef UITIL_H
#define UITIL_H

#ifdef WIN32
#include <Windows.h>
#include <wingdi.h>
#include <tlhelp32.h>
#include <WinUser.h>
#include <wtsapi32.h>
#include <UserEnv.h>
#endif

#include <string>

class Uitil
{
public:
    Uitil();

    static std::wstring stringToWString(const std::string &string);

    static HANDLE getCurrentUserToken();

    static bool runProgAsCurUser(HANDLE token, const std::string &progPath, const std::string &progArgs);

    static bool RunProgAsCurUserAdminPrivilege(const std::string &progPath, const std::string &progArgs);
};

#endif // UITIL_H

#include "uitil.h"

#include <iostream>
#include <locale>
#include <codecvt>

Uitil::Uitil()
{
}

std::wstring Uitil::stringToWString(const std::string &string)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t>> cv;

    return cv.from_bytes(string);
}

HANDLE Uitil::getCurrentUserToken()
{
    // 查询sessionID
    PWTS_SESSION_INFO pSessionInfo = 0;
    DWORD dwCount = 0;
    ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
    int session_id = 0;
    for (DWORD i = 0; i < dwCount; ++i)
    {
        WTS_SESSION_INFO si = pSessionInfo[i];
        if (WTSActive == si.State)
        {
            session_id = si.SessionId;
            break;
        }
    }

    ::WTSFreeMemory(pSessionInfo);

    // 查询token
    HANDLE current_token = 0;
    BOOL bRet = ::WTSQueryUserToken(session_id, &current_token);
    if (bRet == FALSE)
    {
        std::cout << "WTSQueryUserToken error, code:" << GetLastError() << std::endl;

        return nullptr;
    }

    HANDLE primaryToken = 0;
    bRet = ::DuplicateTokenEx(current_token, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &primaryToken);
    ::CloseHandle(current_token);
    if (bRet == FALSE)
    {
        std::cout << "DuplicateTokenEx error, code:" << GetLastError() << std::endl;

        return nullptr;
    }

    return primaryToken;
}

bool Uitil::runProgAsCurUser(HANDLE token, const std::string &progPath, const std::string &progArgs)
{
    STARTUPINFO StartupInfo = {0};
    PROCESS_INFORMATION processInfo;
    StartupInfo.cb = sizeof(STARTUPINFO);

    auto command = std::string("\"") + progPath + "\"";
    if (!progArgs.empty())
    {
        command += " " + progArgs;
    }

    void* lpEnvironment = NULL;
    BOOL resultEnv = ::CreateEnvironmentBlock(&lpEnvironment, token, FALSE);
    if (!resultEnv)
    {
        std::cout << "CreateEnvironmentBlock error, code:" << GetLastError() << std::endl;

        return false;
    }

    std::cout << "runProgAsCurUser, command:" << command << std::endl;

    // 获取到的hUnfilteredToken就是不受限的token,以token作为CreateProcessAsUser的第一个参数,就可以创建出具有管理员权限,并且属于当前用户的界面程序了,并且这种情况下,不需要加入窗口站。
    BOOL result = ::CreateProcessAsUser(token, 0, const_cast<LPWSTR>(Uitil::stringToWString(command).c_str()), NULL, NULL, FALSE, CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, 0, &StartupInfo, &processInfo);
    if(!result)
    {
        std::cout << "CreateProcessAsUser error, code:" << GetLastError() << std::endl;

        return false;
    }

    if(lpEnvironment != NULL)
    {
        ::DestroyEnvironmentBlock(lpEnvironment);
    }

    return true;
}

bool Uitil::RunProgAsCurUserAdminPrivilege(const std::string &progPath, const std::string &progArgs)
{
    // UAC开启时,当前用户拥有两个token,分别是受限的token和不受限的token。explorer.exe进程的token就属于受限的token。
    // 在服务程序中,可以用下面代码获取到受限的token。
    HANDLE primaryToken = getCurrentUserToken();
    if (primaryToken == 0)
    {
        std::cout << "GetCurrentUserToken error." << std::endl;

        return false;
    }

    // 由此token可以得到不受限的token
    bool isOpenOk = false;
    HANDLE hUnfilteredToken = NULL;
    DWORD dwSize = 0;
    BOOL bRet = ::GetTokenInformation(primaryToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);
    if (bRet)
    {
        isOpenOk = runProgAsCurUser(hUnfilteredToken, progPath, progArgs);

        ::CloseHandle(hUnfilteredToken);
    }
    else
    {
        // UAC未开时,继续使用原来的token打开
        std::cout << "GetTokenInformation error and continue open width primary token. code:" << GetLastError() << std::endl;

        isOpenOk = runProgAsCurUser(primaryToken, progPath, progArgs);
    }

    ::CloseHandle(primaryToken);

    return isOpenOk;
}

#include <iostream>
#include <Windows.h>
#include <process.h>
#include <processthreadsapi.h>
#include <tlhelp32.h>
#include <tchar.h>
#include <comutil.h>
#include <cwchar>
#include "ini.h"

#pragma warning(disable:4996)

DWORD FindProcessId(char* processName)
{
	// strip path

	char* p = strrchr(processName, '\\');
	if (p)
		processName = p + 1;

	PROCESSENTRY32 processInfo;
	processInfo.dwSize = sizeof(processInfo);

	HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	if (processesSnapshot == INVALID_HANDLE_VALUE)
		return 0;

	Process32First(processesSnapshot, &processInfo);
	if (!strcmp(processName, processInfo.szExeFile))
	{
		CloseHandle(processesSnapshot);
		return processInfo.th32ProcessID;
	}

	while (Process32Next(processesSnapshot, &processInfo))
	{
		if (!strcmp(processName, processInfo.szExeFile))
		{
			CloseHandle(processesSnapshot);
			return processInfo.th32ProcessID;
		}
	}

	CloseHandle(processesSnapshot);
	return 0;
}

int main()
{
	ini::iniReader config;
	std::string tmp = _pgmptr;
	tmp = tmp.erase(tmp.find_last_of("\\"));
	bool ret = config.ReadConfig(tmp + "\\config.ini");
	if (ret == false)
	{
		printf("ReadConfig is Error,Cfg=%s", "config.ini");
		return 1;
	}
	std::string apppath = config.ReadString("group1", "path", "");
	std::string appname = config.ReadString("group1", "application", "");

	while (!FindProcessId((char *)appname.c_str())) {
		Sleep(200);
		Uitil::RunProgAsCurUserAdminPrivilege(apppath + appname, "");
		int a = 1;
	}
	
	return 1;
}

#ifndef INI_H_
#define INI_H_
#include <string>
#include <map>
#include<fstream>

namespace ini
{
	class iniReader
	{
	public:
		iniReader()
		{
		}
		~iniReader()
		{
		}
		bool ReadConfig(const std::string& filename)
		{
			settings_.clear();
			std::ifstream infile(filename.c_str());//构造默认调用open,所以可以不调用open
			//std::ifstream infile;
			//infile.open(filename.c_str());
			//bool ret = infile.is_open()
			if (!infile) {
				return false;
			}
			std::string line, key, value, section;
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			while (getline(infile, line))
			{
				if (AnalyseLine(line, section, key, value))
				{
					it = settings_.find(section);
					if (it != settings_.end())
					{
						k_v[key] = value;
						it->second = k_v;
					}
					else
					{
						k_v.clear();
						settings_.insert(std::make_pair(section, k_v));
					}
				}
				key.clear();
				value.clear();
			}
			infile.close();
			return true;
		}

		std::string ReadString(const char* section, const char* item, const char* default_value)
		{
			std::string tmp_s(section);
			std::string tmp_i(item);
			std::string def(default_value);
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::string>::iterator it_item;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			it = settings_.find(tmp_s);
			if (it == settings_.end())
			{
				//printf("111");
				return def;
			}
			k_v = it->second;
			it_item = k_v.find(tmp_i);
			if (it_item == k_v.end())
			{
				//printf("222");
				return def;
			}
			return it_item->second;
		}

		int ReadInt(const char* section, const char* item, const int& default_value)
		{
			std::string tmp_s(section);
			std::string tmp_i(item);
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::string>::iterator it_item;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			it = settings_.find(tmp_s);
			if (it == settings_.end())
			{
				return default_value;
			}
			k_v = it->second;
			it_item = k_v.find(tmp_i);
			if (it_item == k_v.end())
			{
				return default_value;
			}
			return atoi(it_item->second.c_str());
		}

		float ReadFloat(const char* section, const char* item, const float& default_value)
		{
			std::string tmp_s(section);
			std::string tmp_i(item);
			std::map<std::string, std::string> k_v;
			std::map<std::string, std::string>::iterator it_item;
			std::map<std::string, std::map<std::string, std::string> >::iterator it;
			it = settings_.find(tmp_s);
			if (it == settings_.end())
			{
				return default_value;
			}
			k_v = it->second;
			it_item = k_v.find(tmp_i);
			if (it_item == k_v.end())
			{
				return default_value;
			}
			return atof(it_item->second.c_str());
		}

	private:
		bool IsSpace(char c)
		{
			if (' ' == c || '\t' == c)
				return true;
			return false;
		}

		bool IsCommentChar(char c)
		{
			switch (c)
			{
			case '#':
				return true;
			default:
				return false;
			}
		}

		void Trim(std::string& str)
		{
			if (str.empty())
			{
				return;
			}
			int i, start_pos, end_pos;
			for (i = 0; i < str.size(); ++i) {
				if (!IsSpace(str[i])) {
					break;
				}
			}
			if (i == str.size())
			{
				str = "";
				return;
			}
			start_pos = i;
			for (i = str.size() - 1; i >= 0; --i) {
				if (!IsSpace(str[i])) {
					break;
				}
			}
			end_pos = i;
			str = str.substr(start_pos, end_pos - start_pos + 1);
		}

		bool AnalyseLine(const std::string& line, std::string& section, std::string& key, std::string& value)
		{
			if (line.empty())
				return false;
			int start_pos = 0, end_pos = line.size() - 1, pos, s_startpos, s_endpos;
			if ((pos = line.find("#")) != -1)
			{
				if (0 == pos)
				{
					return false;
				}
				end_pos = pos - 1;
			}
			if (((s_startpos = line.find("[")) != -1) && ((s_endpos = line.find("]"))) != -1)
			{
				section = line.substr(s_startpos + 1, s_endpos - 1);
				return true;
			}
			std::string new_line = line.substr(start_pos, start_pos + 1 - end_pos);
			if ((pos = new_line.find('=')) == -1)
				return false;
			key = new_line.substr(0, pos);
			value = new_line.substr(pos + 1, end_pos + 1 - (pos + 1));
			Trim(key);
			if (key.empty()) {
				return false;
			}
			Trim(value);
			if ((pos = value.find("\r")) > 0)
			{
				value.replace(pos, 1, "");
			}
			if ((pos = value.find("\n")) > 0)
			{
				value.replace(pos, 1, "");
			}
			return true;
		}

	private:
		//std::map<std::string, std::string> settings_;
		std::map<std::string, std::map<std::string, std::string> >settings_;
	};
}
#endif
[group1]
path = C:\\Users\\EEO\\source\\repos\\Project2\\Debug\\
application = Project2.exe

三、问题的解决方案

下面就如何优化启动时间,做一个详细的说明

1、为什么腾讯会议Rooms那么快

在观察了腾讯会议的开机自启动现象后,发现他的启动非常稳定,都是在开机登录后,五到十秒间完成自启动,在此同时,其他软件都比较慢,如有道云笔记、企业微信等等,那为什么会有如此现象呢?

为了探究该问题的原因,初步分析了腾讯会议是如何实现自启动的,发现他也是同过注册表实现的,只不过他是在WOW6432Node下面,然后就怀疑莫非在这个节点下比较快

 为了验证猜想,制作了一个demo程序,没有任何复杂逻辑,启动后单纯的显示一个窗口,可排除软件自身效率引入的问题干扰,但通过实验发现,启动时间和其他比较慢的程序一样。进一步分析,为了排除那些未知因素,将demo的启动放入腾讯会议的注册表中

结果依然比较慢,那么到底是为什么腾讯会议就启动的比别的软件快呢?还做了其他无用的验证,比如将腾讯会议的启动项和demo调换等等,不在赘述,这些最终都没定位到真实原因。

偶然间发现,在自启动后,将腾讯会议最小化,过个十几二十秒后,腾讯会议会自动再次弹出来,而且这个时间点和其他自启动比较慢的软件的时间点是一致的,所以怀疑其在注册表启动前,通过某种手段已经启动了一次程序,那么是那种手段呢?自启动常见的一共也就上面四种,排查了本地电脑方式一和方式二中没有找到腾讯会议相关的设置,那么剩下方式四了,翻看腾讯会议安装包中的都有哪些exe文件,结合资源管理器中腾讯会议相关的进程,然后再windows服务列表中查找 

 

最终定位到可疑服务 

为了验证猜想,将该服务设置为手动启动,然后发现,腾讯会议的启动和其他程序没有什么差别,都很慢,至此,腾讯会议为什么比较快差不多有结论了。

猜测腾讯会议快速自启动的实现方案:通过开机自启的服务,拉起腾讯会议的应用程序,怎么实现的,可参考前文。(至于腾讯会议是否是这么玩的,这个就不清楚了,但能确定的是,肯定和服务有关)

2.我们是否可以跟腾讯会议一样快

可以的,虽然直接将UI程序做成服务,在启动后循环检测目标进程是否启动,没有了尝试启动,已启动就可以退出服务了。为什么要这么做呢,是因为在实际的验证过程中,发现,在登录前试图拉起目标进程总是失败。

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

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

相关文章

5. 操作系统基础

5. 操作系统基础 常考面试题 说说你对进程的理解⭐⭐⭐ 程序是指令、数据及其组织形式的描述,而进程则是程序的运行实例,包括程序计数器、寄存器和变量的当前值。 Linux的进程结构,一般分为三部分:代码段、数据段(.data与.bss)和堆栈段。 代码段用于存放程序代码,如果有…

武忠祥老师每日一题||不定积分基础训练(六)

解法一&#xff1a; 求出 f ( x ) , 进而对 f ( x ) 进行积分。 求出f(x),进而对f(x)进行积分。 求出f(x),进而对f(x)进行积分。 令 ln ⁡ x t , 原式 f ( t ) ln ⁡ ( 1 e t ) e t 令\ln xt,原式f(t)\frac{\ln (1e^t)}{e^t} 令lnxt,原式f(t)etln(1et)​ 则 ∫ f ( x ) d…

java学习之枚举二

目录 一、enum关键字实现枚举 二、注意事项 一、对Season2进行反编译&#xff08;javap&#xff09; ​编辑 三、练习题 第一题 第二题 一、enum关键字实现枚举 package enum_;public class Enumeration03 {public static void main(String[] args) {System.out.println…

Python每日一练(20230506) 存在重复元素I、II、III

目录 1. 存在重复元素 Contains Duplicate I 2. 存在重复元素 Contains Duplicate II 3. 存在重复元素 Contains Duplicate III &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 存在重…

【Linux 裸机篇(八)】I.MX6U EPIT 定时器中断、定时器按键消抖

目录 一、EPIT 定时器简介二、定时器按键消抖 一、EPIT 定时器简介 EPIT 的全称是&#xff1a; Enhanced Periodic Interrupt Timer&#xff0c;直译过来就是增强的周期中断定时器&#xff0c;它主要是完成周期性中断定时的。学过 STM32 的话应该知道&#xff0c; STM32 里面的…

电脑系统怎么选?Win?MacOS?Linux?

马上要学编程了&#xff0c;我们要学什么操作系统呢&#xff1f;是MacOS&#xff0c;还是Windows&#xff0c;或者是Linux或者其他&#xff01;那我们今天就来说说MacOS系统和Windows系统的优缺点&#xff0c;也介绍一下其他的系统。让你心里有底&#xff01; 1、Windows 首先当…

Neo4j导出和导入数据库

Neo4j 4.x版本和5.x版本的导出导入有区别&#xff0c;这里分开来讲。 1 4.x版本 1.1 准备 导入导出之前要先关闭neo4j服务。 .neo4j stop 1.2 数据导出 进入$NEO4J_HOME%/bin目录执行如下数据库导出命令&#xff1a; neo4j-admin dump --databaseneo4j --toF:/neo4j_bac…

《Netty》从零开始学netty源码(五十四)之PoolThreadLocalCache

PoolThreadLocalCache 前面讲到PoolThreadCache&#xff0c;它为线程提供内存缓存&#xff0c;当线程需要分配内存时可快速从其中获取&#xff0c;在Netty中用PoolThreadLocalCache来管理PoolThreadCache&#xff0c;它的数据结构如下&#xff1a; PoolThreadLocalCache相当…

Unity3D:内置着色器的用途和性能

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 内置着色器的用途和性能 Unity 中的着色器是通过__材质__来使用的&#xff0c;材质本质上结合了着色器代码与纹理等参数。此处提供了关于着色器/材质关系的深入说明。 当选择材质…

延时队列的三种实现方案

延时队列的三种实现方案 什么是延时队列延时队列的应用场景基于Java DelayQueue的实现源码剖析 基于Redis的zset实现实现步骤Redis延时队列优势Redis延时队列劣势 基于RabbitMQ的延时队列实现TTL DXL(死信队列)插件实现 总结参考文章 什么是延时队列 在分布式系统中&#xff…

Java之多线程初阶2

目录 一.上节内容复习 1.进程和线程的区别 2.创建线程的四种方式 二.多线程的优点的代码展示 1.多线程的优点 2.代码实现 三.Thread类常用的方法 1.Thread类中的构造方法 2.Thread类中的属性 1.为线程命名并获取线程的名字 2.演示isDaemon() 3.演示isAlive() 4.演示…

ChatGPT写文章效果-ChatGPT写文章原创

ChatGPT写作程序&#xff1a;让文案创作更轻松 在当前数字化的时代&#xff0c;营销推广离不开文案创作。然而&#xff0c;写作对许多人来说可能是一项耗时而枯燥的任务。如果您曾经为写出较高质量的文案而苦恼过&#xff0c;那么ChatGPT写作程序正是为您而设计的。 ChatGPT是…

Python 模块

目录 1.模块导入语言 1.1 import 语句 1.2 from…import 语句​编辑 2. 搜索路径 3.命名空间和作用域 4.globals() 和 locals() 函数 5.reload() 函数 6.Python中的包 7.自定义模块及其调用 7.1 创建模块及__init__.py初始化文件 7.2 __init__.py的参数__all__ …

【vite+vue3.2 项目性能优化实战】打包体积分析插件rollup-plugin-visualizer视图分析

rollup-plugin-visualizer是一个用于Rollup构建工具的插件&#xff0c;它可以生成可视化的构建报告&#xff0c;帮助开发者更好地了解构建过程中的文件大小、依赖关系等信息。 使用rollup-plugin-visualizer插件&#xff0c;可以在构建完成后生成一个交互式的HTML报告&#xf…

从血缘进化论的角度,破解婆媳关系的世纪难题

从血缘进化论的角度&#xff0c;破解婆媳关系的世纪难题 有个粉丝的留言&#xff0c;很长很复杂&#xff0c;是关于他们家的婆媳关系问题。 青木老师&#xff0c;您好&#xff0c;我也有一些问题想咨询您&#xff0c;是关于婆媳关系的&#xff0c;字数有些多&#xff0c;分开…

【ElasticSearch】EQL操作相关

文章目录 EQL操作基础语法数据准备数据窗口搜索统计符合条件的事件事件序列 安全检测数据准备查看数据导入情况获取 regsvr32 事件的计数检查命令行参数检查恶意脚本加载检查攻击成功可能性 EQL操作 EQL 的全名是 Event Query Language (EQL)。事件查询语言&#xff08;EQL&…

【问题记录】flask开发blog

文章目录 小知识点问题1. 文章标签显示错误2. 文章状态无法回显&#xff08;open)3. 用户管理页面&#xff0c;图标无法显示4. BuildError5. 用户管理添加用户&#xff0c;使用重复的用户名会报错(open)6. 添加用户&#xff0c;不上传头像会报错(open)7. 部分标签删除时报错&am…

JAVA springboot创业实践学分管理系统idea开发mysql数据库web结构计算机java编程MVC

一、源码特点 idea springboot创业实践学分管理系统是一套完善的web设计系统mysql数据库MVC模式开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式 开发。 JAVA springboot创业实践学分管理系统ide…

Ubuntu搜狗输入法安装指南

Ubuntu搜狗输入法安装指南 Ubuntu搜狗输入法安装指南搜狗输入法已支持Ubuntu1604、1804、1910、2004、2010Ubuntu20.04及以上安装搜狗输入法步骤 Ubuntu搜狗输入法安装指南 下载地址&#xff1a;https://shurufa.sogou.com/ 计算为amd64的选择x86_64&#xff0c;以下教程来源…

2023Java商城毕业设计(附源码和数据库文件下载链接)Spring Boot + mysql + maven + mybatis-plus

2023Java商城毕业设计Spring Boot mysql maven mybatis-plus 用户注册用户登录修改密码商品列表&#xff08;分类模糊查询&#xff09;个人信息用户信息修改订单信息添加至购物车商品列表商铺详情商品详情商铺列表 资源目录如下&#xff1a;&#xff08;源码sql文件&#xf…