一,要实现的效果
- 版本更新后发布最新的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;
//qxl
BPtr<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
#endif
if (App()->IsPortableMode())
execInfo.lpParameters = UPDATE_ARG_SUFFIX L" Portable";
else
execInfo.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.exe
if (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);
//core
json_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();
}
}
版权声明:本文为weixin_38416696原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。