OBS 实现强制升级功能

article/2025/9/27 15:16:34

一,要实现的效果

  •  版本更新后发布最新的OBS版本 到阿里云
  • 用户端打开OBS时,强制让用户从阿里云更新最新版本

二,OBS升级 底层逻辑

三,修改的地方

win-update.cpp

//升级 线程
void AutoUpdateThread::run()
try {long           responseCode;vector<string> extraHeaders;string         text;string         error;string         signature;CryptProvider  localProvider;BYTE           manifestHash[BLAKE2_HASH_LENGTH];bool           updatesAvailable = false;bool           success;struct FinishedTrigger {inline ~FinishedTrigger(){QMetaObject::invokeMethod(App()->GetMainWindow(),"updateCheckFinished");	}} finishedTrigger;BPtr<char> manifestPath = GetConfigPathPtr("obs-studio\\updates\\manifest.json");auto ActiveOrGameCaptureLocked = [this] (){if (obs_video_active()) {if (manualUpdate)info(QTStr("Updater.Running.Title"),QTStr("Updater.Running.Text"));return true;}if (IsGameCaptureInUse()) {if (manualUpdate)info(QTStr("Updater.GameCaptureActive.Title"),QTStr("Updater.GameCaptureActive.Text"));return true;}return false;};/* ----------------------------------- ** warn if running or gc locked        */if (ActiveOrGameCaptureLocked())return;/* ----------------------------------- ** create signature provider           */if (!CryptAcquireContext(&localProvider,nullptr,MS_ENH_RSA_AES_PROV,PROV_RSA_AES,CRYPT_VERIFYCONTEXT))throw strprintf("CryptAcquireContext failed: %lu",GetLastError());provider = localProvider;/* ----------------------------------- ** avoid downloading manifest again    */if (CalculateFileHash(manifestPath, manifestHash)) {char hashString[BLAKE2_HASH_STR_LENGTH];HashToString(manifestHash, hashString);string header = "If-None-Match: ";header += hashString;extraHeaders.push_back(move(header));}/* ----------------------------------- ** get current install GUID            */string guid = GetProgramGUID();if (!guid.empty()) {string header = "X-OBS2-GUID: ";header += guid;extraHeaders.push_back(move(header));}/**************************************************************************/success = GetRemoteFileFromAliyun("updates/config/manifest.json", manifestPath);/* ----------------------------------- ** get manifest from server            *///success = GetRemoteFile(WIN_MANIFEST_URL, text, error, &responseCode,/*		nullptr, nullptr, extraHeaders, &signature);if (!success || (responseCode != 200 && responseCode != 304)) {if (responseCode == 404)return;throw strprintf("Failed to fetch manifest file: %s", error.c_str());}*//* ----------------------------------- ** verify file signature               *//* a new file must be digitally signed  校验数字签名 *//*if (responseCode == 200) {success = CheckDataSignature(text, "manifest",signature.data(), signature.size());if (!success)throw string("Invalid manifest signature");}*//**************************************************************************//* ----------------------------------- ** write or load manifest          读取mainfest文件    */ //if (/*responseCode == 200*/success) {//	if (!QuickWriteFile(manifestPath, text.data(), text.size()))//		throw strprintf("Could not write file '%s'",//				manifestPath.Get());//} else {if (!QuickReadFile(manifestPath, text))throw strprintf("Could not read file '%s'",manifestPath.Get());//}/* ----------------------------------- ** check manifest for update           */string notes;int updateVer = 0;success = ParseUpdateManifest(text.c_str(), &updatesAvailable, notes,updateVer);//比对版本 判断是否更新if (!success)throw string("Failed to parse manifest");if (!updatesAvailable) {//if (manualUpdate)//	info(QTStr("Updater.NoUpdatesAvailable.Title"),//     QTStr("Updater.NoUpdatesAvailable.Text"));return;}/* ----------------------------------- ** skip this version if set to skip    */int skipUpdateVer = config_get_int(GetGlobalConfig(), "General","SkipUpdateVersion");if (!manualUpdate && updateVer == skipUpdateVer)return;/* ----------------------------------- ** warn again if running or gc locked  */if (ActiveOrGameCaptureLocked())return;/* ----------------------------------- ** fetch updater module            获取updater.exe     *///if (!FetchUpdaterModule(WIN_UPDATER_URL))//	return;//qxlBPtr<char> updateFilePath = GetConfigPathPtr("obs-studio\\updates\\libcurl.dll");if (!GetRemoteFileFromAliyun("updates/config/libcurl.dll", updateFilePath))return;updateFilePath = GetConfigPathPtr("obs-studio\\updates\\libeay32.dll");if (!GetRemoteFileFromAliyun("updates/config/libeay32.dll", updateFilePath))return;updateFilePath = GetConfigPathPtr("obs-studio\\updates\\ssleay32.dll");if (!GetRemoteFileFromAliyun("updates/config/ssleay32.dll", updateFilePath))return;updateFilePath = GetConfigPathPtr("obs-studio\\updates\\zlibwapi.dll");if (!GetRemoteFileFromAliyun("updates/config/zlibwapi.dll", updateFilePath))return;updateFilePath = GetConfigPathPtr("obs-studio\\updates\\updater.exe");if (!GetRemoteFileFromAliyun("updates/config/updater.exe", updateFilePath))return;/* ----------------------------------- ** query user for update               *///不询问用户,直接下载更新/*int queryResult = queryUpdate(manualUpdate, notes.c_str());if (queryResult == OBSUpdate::No) {if (!manualUpdate) {long long t = (long long)time(nullptr);config_set_int(GetGlobalConfig(), "General","LastUpdateCheck", t);}return;} else if (queryResult == OBSUpdate::Skip) {config_set_int(GetGlobalConfig(), "General","SkipUpdateVersion", updateVer);return;}*//* ----------------------------------- ** get working dir                     */wchar_t cwd[MAX_PATH];GetModuleFileNameW(nullptr, cwd, _countof(cwd) - 1);wchar_t *p = wcsrchr(cwd, '\\');if (p)*p = 0;/* ----------------------------------- ** execute updater                     */ //确认对话框开始更新//qxl//BPtr<char> updateFilePath = GetConfigPathPtr(//"obs-studio\\updates\\updater.exe");BPtr<wchar_t> wUpdateFilePath;size_t size = os_utf8_to_wcs_ptr(updateFilePath, 0, &wUpdateFilePath);if (!size)throw string("Could not convert updateFilePath to wide");/* note, can't use CreateProcess to launch as admin. */SHELLEXECUTEINFO execInfo = {};execInfo.cbSize = sizeof(execInfo);execInfo.lpFile = wUpdateFilePath;
#ifndef UPDATE_CHANNEL
#define UPDATE_ARG_SUFFIX L""
#else
#define UPDATE_ARG_SUFFIX UPDATE_CHANNEL
#endifif (App()->IsPortableMode())execInfo.lpParameters = UPDATE_ARG_SUFFIX L" Portable";elseexecInfo.lpParameters = UPDATE_ARG_SUFFIX;execInfo.lpDirectory = cwd;execInfo.nShow       = SW_SHOWNORMAL;//执行 updater.exe 文件if (!ShellExecuteEx(&execInfo)) {QString msg = QTStr("Updater.FailedToLaunch");info(msg, msg);throw strprintf("Can't launch updater '%s': %d",updateFilePath.Get(), GetLastError());}/* force OBS to perform another update check immediately after updating* in case of issues with the new version */config_set_int(GetGlobalConfig(), "General", "LastUpdateCheck", 0);config_set_int(GetGlobalConfig(), "General", "SkipUpdateVersion", 0);config_set_string(GetGlobalConfig(), "General", "InstallGUID",guid.c_str());QMetaObject::invokeMethod(App()->GetMainWindow(), "close");} catch (string text) {blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str());
}

update.cpp

//程序入口
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
{//解析参数 生成json文件LPWSTR *szArgList;int argCount;szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);if (szArgList == NULL){MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK);return 10;}//for (int i = 0; i < argCount; i++)//{//	MessageBox(NULL, szArgList[i], L"Arglist contents", MB_OK);//}bool isJson = false;if (argCount == 3 ) {char *parm = ConvertLPWSTRToLPSTR(szArgList[1]);if (string(parm) == "-json") {string path = ConvertLPWSTRToLPSTR(szArgList[2]);GenerateJson(path);isJson = true;}}LocalFree(szArgList);if (isJson)return 10;INITCOMMONCONTROLSEX icce;if (!HasElevation()) { //管理员启动 则不会进入HANDLE hLowMutex = CreateMutexW(nullptr, true,L"OBSUpdaterRunningAsNonAdminUser");RestartAsAdmin(lpCmdLine);//管理员用户启动  updater.exeif (hLowMutex) {ReleaseMutex(hLowMutex);CloseHandle(hLowMutex);}return 0;} else{{wchar_t cwd[MAX_PATH];wchar_t newPath[MAX_PATH];GetCurrentDirectoryW(_countof(cwd) - 1, cwd);is32bit = wcsstr(cwd, L"bin\\32bit") != nullptr;StringCbCat(cwd, sizeof(cwd), L"\\..\\..");GetFullPathName(cwd, _countof(newPath), newPath,nullptr);SetCurrentDirectory(newPath);}hinstMain = hInstance;icce.dwSize = sizeof(icce);icce.dwICC  = ICC_PROGRESS_CLASS;InitCommonControlsEx(&icce);hwndMain = CreateDialog(hInstance,MAKEINTRESOURCE(IDD_UPDATEDIALOG), nullptr,UpdateDialogProc);if (!hwndMain) {return -1;}ShowWindow(hwndMain, SW_SHOWNORMAL);SetForegroundWindow(hwndMain);cancelRequested = CreateEvent(nullptr, true, false, nullptr);//执行 更新线程updateThread = CreateThread(nullptr, 0, UpdateThread,lpCmdLine, 0, nullptr);MSG msg;while (GetMessage(&msg, nullptr, 0, 0)) {if (!IsDialogMessage(hwndMain, &msg)) {TranslateMessage(&msg);DispatchMessage(&msg);}}/* there is no non-elevated process waiting for us if UAC is* disabled */WinHandle hMutex = OpenMutex(SYNCHRONIZE, false,L"OBSUpdaterRunningAsNonAdminUser");if (msg.wParam == 1 && !hMutex) {LaunchOBS();}return (int)msg.wParam;}
}

manifest.json 文件生成

void GenerateJson(string path) {// 遍历文件runDirPath = path;//"G:\\obs-studio\\obs-23.2.1\\build\\rundir\\Debug\\";char* p = const_cast<char*>(runDirPath.c_str());strcat(p, "*.*");fileSearch(p, 0);//生成hash值std::map<string, string> fileHastMap;if (fileSizeMap.size() > 0) {for (auto it = fileSizeMap.begin(); it != fileSizeMap.end(); ++it) {uint8_t  existingHash[BLAKE2_HASH_LENGTH];CalculateFileHash(char2wchar(it->first.c_str()), existingHash);char hashString[BLAKE2_HASH_STR_LENGTH];HashToStringTwo(existingHash, hashString);fileHastMap[it->first] = string(hashString);}}//生成json文件json_t *obj = json_object();json_object_set(obj, "notes", json_string("test"));json_object_set(obj, "version_major", json_integer(0));json_object_set(obj, "version_minor", json_integer(0));json_object_set(obj, "version_patch", json_integer(1));json_t *objPackages = json_array();json_object_set(obj, "packages", objPackages);//corejson_t *objCore = json_object();json_t *objFiles = json_array();if (fileSizeMap.size() > 0) {for (auto it = fileSizeMap.begin(); it != fileSizeMap.end(); ++it) {string name = it->first;int size = it->second;string hash;if (fileHastMap.count(name) == 1) {hash = fileHastMap[name];}//int strSize = runDirPath.size();string subPath = name.substr(strSize);for (size_t i = 0; i<subPath.size(); i++) {if (subPath[i] == '\\') {subPath.replace(i, 1, string("\/"));i++;}}json_t *objFile = json_object();json_object_set_new(objFile, "name", json_string(subPath.c_str()));json_object_set_new(objFile, "hash", json_string(hash.c_str()));json_object_set_new(objFile, "size", json_integer(size));json_array_append_new(objFiles, objFile);}}json_object_set_new(objCore, "files", objFiles);json_object_set_new(objCore, "name", json_string("core"));//json_array_append_new(objPackages, objCore);string newManifest;if (json_object_size(obj) > 0) {char *post_body = json_dumps(obj, JSON_COMPACT);int    responseCode;int len = (int)strlen(post_body);uLong compressSize = compressBound(len);string compressedJson;compressedJson.resize(compressSize);compress2((Bytef*)&compressedJson[0], &compressSize,(const Bytef*)post_body, len,Z_BEST_COMPRESSION);compressedJson.resize(compressSize);newManifest = string(post_body);free(post_body);if (obj)json_delete(obj);}else {newManifest = "[]";}//保存json文件if (!newManifest.empty()) {ofstream fout;fout.open("F:\\manifest.json", ios_base::out | ios_base::trunc);fout << newManifest << endl;fout.close();}
}

 

 


http://chatgpt.dhexx.cn/article/jupJlnLD.shtml

相关文章

绕过限制,强制升级至Windows11

目录 以前…… 开始升级。 新界面预览 附&#xff1a;文档下载 注意:本教程使用Windows11 insider preview,可能会有系统崩溃等现象。如需安装普通版本,请参考此处。 以前…… 以前的强制升级是这样的&#xff1a; 点击此链接查看详情&#xff1a;http://www.pccppc.com/…

真我GT realme GT root权限 解锁BL 刷ROOT教程 免费 TWRP

realme GT root后变砖了 也可以联系技术远程救砖 http://www.aialbb.com/thread-1443-1-1.html 可以9008 刷机 开机 解决不开机问题 realme GT解锁及ROOT教程 刷机有风险 入门需谨慎 仅限GT 文件下载总链接查看链接 机型:realme真我GT 代号:RMX2202 地区:中国CN A.解锁BL锁 1…

谷歌系列手机刷机和面具ROOT教程

目录 一、环境介绍二、准备工具2.1 下载官方ROM2.2 下载Magisk文件2.3 下载TWRP文件 三、刷机四、安装面具五、总结 一、环境介绍 1.一台真机&#xff1a;本文使用的是pixel 1代 2.adb环境的配置&#xff1a;安装Android Stadio(AS)后&#xff0c;都会配置。 3.需要安装的镜像(…

Ubuntu的root

Ubuntu的root 介绍给root用户设置密码并使用引用 介绍 安装ubuntu成功后&#xff0c;都是普通用户权限&#xff0c;并没有最高root权限&#xff0c;如果需要使用root权限的时候&#xff0c;通常都会在命令前面加上 sudo。有的时候感觉很麻烦。 我们一般使用su命令来直接切换到…

一加6T手机Android10 root教程

一加六T手机Android10 root教程 刷机之前一定要备份&#xff01;备份&#xff01;备份&#xff01; 第一步&#xff1a;准备文件第二步&#xff1a;准备工作第三步&#xff1a;解BL锁第四步&#xff1a;刷入第三方TWRP第五步&#xff1a;刷入Magisk 前言&#xff1a;一加6T手机…

root全攻略(root是什么 怎么root root能干什么)

首先讲什么是root 为了手机安全 我们是无法对手机系统进行修改 为的是防止用户的误操作比如直接把system删了 导致手机废掉 取得root就是取得android手机的最高控制权 很类似塞班的xx&#xff08;破解&#xff09; root好处 有了最高控制权 就可以修改手机所有地方 只要你有能力…

root手机教程

以前的root基本是su root&#xff0c;但是su不再维护更新之后&#xff0c;就有了现在的比su更好玩的root手机的工具→magisk。Magisk具备很强大的模块扩展&#xff0c;可以不修改system区的文件&#xff0c;达到修改system文件一样的效果&#xff0c;例如换字体&#xff0c;换主…

用安卓手机三步获取root权限教程

因为我有时候装一些软件需要手机的Root权限&#xff0c;而现在一些手机获取有非常的的麻烦&#xff0c;网上查怎样获取的时候各种广告各种坑&#xff0c;下载什么什么一件获得root权限的APP一般都是打广告的流氓软件&#xff0c;安装装后还给你装其它一大堆软件&#xff0c;也根…

Android 系统root教程-magisk最新版

Android 系统root教程-magisk最新版 前言 Magisk之前的root方式都需要通过twrp去刷入文件&#xff0c;而且还有Magisk&#xff08;核心组件&#xff09;和Magisk Manager&#xff08;配套应用程序&#xff09;分开安装&#xff0c;非常麻烦&#xff0c;在Magisk v22.0之后&am…

android手机Root全过程

写在前面 现在的android手机想要获取root的最高权限都是非常麻烦的&#xff0c;当然这样也能让手机相对更安全&#xff0c;但是最近公司一个项目其中的部分功能需要获取手机的root权限&#xff0c;所以这里就做一个root过程的记录。 以红米6为例&#xff0c;说一下root的过程…

华为手机root的最简单方法教程!附专用root工具!详细

华为手机root的最简单方法&#xff1a; 18年那会我做过手机维修师&#xff0c;对于这领域的技术掌握的还是比较多。 如果你也想root你的华为手机&#xff0c;那么建议把这篇文章看完 下面教程需要几个必须的工具&#xff0c;我们已经打包上传好了&#xff0c;去我的公众号“…

android怎样开启root权限管理,手机怎样开启root权限(最新安卓手机一键root教程)...

对于安卓手机用户来说&#xff0c;root权限这个词应该不陌生。在使用手机的过程中&#xff0c;用户难免需要增删一些内容&#xff0c;但是有时候手机就会跳出“需要root权限”这样的字眼&#xff0c;这是怎么回事呢&#xff1f;其实是因为有些手机厂商为了方便推广某些软件&…

android一键root权限获取,怎样获得root权限(一键root手机的教程)

root这个词相信大家都听过&#xff0c;它是安卓系统的超级用户。而获取了root权限&#xff0c;就可访问和修改手机里几乎所有的文件&#xff0c;比如删除无用的预装APP、优化系统运行等。今天我就为大家介绍一款root软件——《KingRoot》&#xff0c;无需专业知识&#xff0c;无…

安卓ROOT全教程(测试机 红米Note7Pro)

0x1.准备工作,申请解锁BL下载工具 小米解BL锁需要在手机上绑定并申请&#xff0c;具体教程如下&#xff0c;自己看&#xff0c;很简单。中途如出现问题&#xff0c;官方都已经汇总贴出解决方案了。 http://www.miui.com/unlock/index.html 解BL锁成功后&#xff0c;下载所需…

安卓设备如何ROOT?玩转ROOT,让你的安卓手机更强更好用

曾经&#xff0c;在我们刚拿到一部新的安卓手机时&#xff0c;都会第一时间选择进行ROOT&#xff0c;在前几年&#xff0c;ROOT可谓是引发了热潮&#xff1b;因为很多手机制造商&#xff0c;会对用户修改操作系统进行限制&#xff0c;ROOT也是因此应运而生。 但是&#xff0c;…

【ROOT from CERN】——ROOT的安装

ROOT 是CERN&#xff08;欧洲核子中心&#xff09;开发的&#xff0c;基于C的数据分析框架&#xff08;语言&#xff09;&#xff0c;在大量数据的情况下表现十分良好。本篇文章将描述有关于ROOT在Linux&#xff08;以Ubuntu为例&#xff09;和Windows&#xff08;以Win10为例&…

root教程_方法

的root教程在这里整理了一下&#xff0c;之前有机友说自己的手机想删除系统自带的一些无用软件&#xff0c;可是怎么也删除不了&#xff0c;所以需要先进行root才可以删除&#xff0c;不然的话是 删除不了的&#xff0c;这个方法也是大家在root过程中总结出来了&#xff0c;因为…

ADSAD合辑(一)

我同门用了大概4h就从下载ads到照着书画图完成了了一个ads仿真电路。真的大开眼界。太欣赏她了。劈开乱麻就是路&#xff0c;脸皮厚了无所不能哈哈哈&#xff08;这是褒义只是我想不到褒义词了&#xff09;。然后又请一位同学画PCB&#xff0c;我在旁边看了2h&#xff0c;自己实…

Oracle的ADW数据分析课(一)

第一课 一小时开通企业疫情防控系统 1、首先介绍了APEX,什么是APEX? Oracle APEX&#xff1a;Oracle Applation Express 一个低代码开发的平台&#xff0c;可以在该平台上构建可扩展、安全的企业应用。 2、APEX构建WEB应用的技术特点 (1)与Oracle数据库相伴相生 (2)…

AD原理图设计

AD原理图设计 1. (简答题) 1、安装Altium Designer 18&#xff0c;学习使用Altium Designer软件绘制一个stm32最小系统的电路原理图&#xff0c;并在此基础上完成STM32W5500的系统电路原理图设计。(该系统的PCB图的绘制不做要求&#xff0c;凭个人兴趣选做) 参考学习网站&…