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

目录
一、问题的说明和定义
二、问题的分析
1.问题初步分析
2.详细的分析:
2.常见的自启动方式
2.常见的自启动方式的细节分析
三、问题的解决方案
1、为什么腾讯会议Rooms那么快
2.我们是否可以跟腾讯会议一样快
一、问题的说明和定义
这两天有个优化项需要做个技术调研,就是我们项目的开机启动时间比较长,大概20多秒,但对比腾讯会议Rooms,这款软件的自启动就异常的快,用户登录几秒就可以看到程序界面 。
拿到任务了,开干吧!!!!!!
二、问题的分析 1.问题初步分析
首先分析猜测慢的原因:
2.详细的分析: 2.常见的自启动方式
知己知彼百战百胜,所以第一步,先学习了解系统中自启动的方案都有哪些,常见的方式四个
指定文件夹中放入程序快捷方式,或者启动脚本bat文件 。注册表自启动方式任务计划程序方式任务自启动
针对上面四种的详细描述,网上一搜一大把,我这里就不在赘述了,但我想把我在分析过程中,针对这几种方案的差别,做个简单的描述
2.常见的自启动方式的细节分析
针对第一、二种,没啥好说的,个人感觉,私人电脑自己配置可以这么玩,简单粗暴,但针对一款应用软件的功能,这么做就不是很合适了,不推荐
第三种自启动方式,一共有五个注册表类型可以实现自启动,如下:
\\\\\Run

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

文章插图
表示任何账户每一次登陆到系统都会自动启动在这个项下面注册的程序
\\\\\
表示任何账户下一次登陆到系统会自动启动在这个项下面注册的程序,以后就不会自启了
\\\\\Run
表示当前账户每一次登陆到系统都会自动启动在这个项下面注册的程序
\\\\\
表示当前账户下一次登陆到系统会自动启动在这个项下面注册的程序,以后就不会自启了
\\\\\\Run
表示程序需要已管理员权限启动
自启动耗时在哪里呢?
1、注册表自启动的程序是在用户登录后,是否可以提到用户登录前呢?这样就可以提升启动速度了 。针对Win11来说,是可以的,win11有一个控制选项,设置里面,找账户
“使用我的登录信息在更新后自动完成设置” 该选项保持勾选,即可在用户登录前启动
win10应该没有该选项控制
2、操作系统有一个注册表的值,可以控制在用户登录后,延迟若干秒后,在自启动应用程序,该机制主要是为了把计算机资源留给操作系统用,该字段默认是没有的,没有的情况下,操作系统的默认延时未10s,可以加上该字段,然后设置其为0,即可取消这10s的延时\S-1-5-21---1001\\\\\\
项如果没有,需要添加一下,然后再该项下,新增一个DWORD的值,设置16进制为0即可
3、如果自启动项比较多的时候,启动顺序是不可控的,这样肯定会有些排在后面,导致启动比较慢,该问题的解决方案会在后面说
第四种方式就是通过服务启动
网上制作服务的博客很多,不在赘述
把exe 设置成服务运行_exe做成服务_小小的技术员的博客-CSDN博客
该方案的优点是,在计算机启动后,服务会优先启动,无论是否用户登录,缺点是从xp或者win7以后,不支持UI界面的服务,如果将UI程序制作为服务,那么UI部分也是不会启动的,原因可参看[笔记]安全之《二》隔离及相关启动技术_会话隔离_二进制怪兽的博客-CSDN博客
下面是启动指定app的服务程序,可参考
Windows 程序开机自启动速度优化,为什么腾讯会议自启动速度那么高?

文章插图
#ifndef UITIL_H#define UITIL_H#ifdef WIN32#include #include #include #include #include #include #endif#include 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 #include #include Uitil::Uitil(){}std::wstring Uitil::stringToWString(const std::string &string){std::wstring_convert> cv;return cv.from_bytes(string);}HANDLE Uitil::getCurrentUserToken(){// 查询sessionIDPWTS_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);// 查询tokenHANDLE current_token = 0;BOOL bRet = ::WTSQueryUserToken(session_id, ¤t_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(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可以得到不受限的tokenbool 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 #include #include #include #include #include #include #include #include "ini.h"#pragma warning(disable:4996)DWORD FindProcessId(char* processName){// strip pathchar* 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 #include #includenamespace 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 k_v;std::map >::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 k_v;std::map::iterator it_item;std::map >::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 k_v;std::map::iterator it_item;std::map >::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 k_v;std::map::iterator it_item;std::map >::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 = http://www.kingceram.com/post/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 settings_;std::map >settings_;};}#endif
[group1]path = C:\\Users\\EEO\\source\\repos\\Project2\\Debug\\application = Project2.exe
三、问题的解决方案
下面就如何优化启动时间,做一个详细的说明
1、为什么腾讯会议Rooms那么快
在观察了腾讯会议的开机自启动现象后,发现他的启动非常稳定,都是在开机登录后,五到十秒间完成自启动,在此同时,其他软件都比较慢,如有道云笔记、企业微信等等,那为什么会有如此现象呢?
为了探究该问题的原因,初步分析了腾讯会议是如何实现自启动的,发现他也是同过注册表实现的,只不过他是在下面,然后就怀疑莫非在这个节点下比较快
为了验证猜想,制作了一个demo程序,没有任何复杂逻辑,启动后单纯的显示一个窗口,可排除软件自身效率引入的问题干扰,但通过实验发现,启动时间和其他比较慢的程序一样 。进一步分析,为了排除那些未知因素,将demo的启动放入腾讯会议的注册表中
结果依然比较慢,那么到底是为什么腾讯会议就启动的比别的软件快呢?还做了其他无用的验证,比如将腾讯会议的启动项和demo调换等等,不在赘述,这些最终都没定位到真实原因 。
偶然间发现,在自启动后,将腾讯会议最小化,过个十几二十秒后,腾讯会议会自动再次弹出来,而且这个时间点和其他自启动比较慢的软件的时间点是一致的,所以怀疑其在注册表启动前,通过某种手段已经启动了一次程序,那么是那种手段呢?自启动常见的一共也就上面四种,排查了本地电脑方式一和方式二中没有找到腾讯会议相关的设置,那么剩下方式四了,翻看腾讯会议安装包中的都有哪些exe文件,结合资源管理器中腾讯会议相关的进程,然后再服务列表中查找
最终定位到可疑服务
为了验证猜想,将该服务设置为手动启动,然后发现,腾讯会议的启动和其他程序没有什么差别,都很慢,至此,腾讯会议为什么比较快差不多有结论了 。
猜测腾讯会议快速自启动的实现方案:通过开机自启的服务,拉起腾讯会议的应用程序,怎么实现的,可参考前文 。(至于腾讯会议是否是这么玩的,这个就不清楚了,但能确定的是,肯定和服务有关)
2.我们是否可以跟腾讯会议一样快
【Windows 程序开机自启动速度优化,为什么腾讯会议自启动速度那么高?】可以的,虽然直接将UI程序做成服务,在启动后循环检测目标进程是否启动,没有了尝试启动,已启动就可以退出服务了 。为什么要这么做呢,是因为在实际的验证过程中,发现,在登录前试图拉起目标进程总是失败 。