Jelajahi Sumber

UI/updater: Add automatic check/install for VS2017 redist

Allows users to seamlessly upgrade the version of OBS Studio to the
newer version which is built with VS2017 rather than VS2013.
jp9000 7 tahun lalu
induk
melakukan
aab6b3f55f
1 mengubah file dengan 200 tambahan dan 0 penghapusan
  1. 200 0
      UI/win-update/updater/updater.cpp

+ 200 - 0
UI/win-update/updater/updater.cpp

@@ -57,6 +57,50 @@ void FreeWinHttpHandle(HINTERNET handle)
 
 /* ----------------------------------------------------------------------- */
 
+static inline bool is_64bit_windows(void);
+
+static inline bool HasVS2017Redist2()
+{
+	wchar_t base[MAX_PATH];
+	wchar_t path[MAX_PATH];
+	WIN32_FIND_DATAW wfd;
+	HANDLE handle;
+	int folder = (is32bit && is_64bit_windows())
+		? CSIDL_SYSTEMX86
+		: CSIDL_SYSTEM;
+
+	SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, base);
+
+	StringCbCopyW(path, sizeof(path), base);
+	StringCbCatW(path, sizeof(path), L"\\msvcp140.dll");
+	handle = FindFirstFileW(path, &wfd);
+	if (handle == INVALID_HANDLE_VALUE) {
+		return false;
+	} else {
+		FindClose(handle);
+	}
+
+	StringCbCopyW(path, sizeof(path), base);
+	StringCbCatW(path, sizeof(path), L"\\vcruntime140.dll");
+	handle = FindFirstFileW(path, &wfd);
+	if (handle == INVALID_HANDLE_VALUE) {
+		return false;
+	} else {
+		FindClose(handle);
+	}
+
+	return true;
+}
+
+static bool HasVS2017Redist()
+{
+	PVOID old = nullptr;
+	bool redirect = !!Wow64DisableWow64FsRedirection(&old);
+	bool success = HasVS2017Redist2();
+	if (redirect) Wow64RevertWow64FsRedirection(old);
+	return success;
+}
+
 static void Status(const wchar_t *fmt, ...)
 {
 	wchar_t str[512];
@@ -882,6 +926,153 @@ static wchar_t tempPath[MAX_PATH] = {};
 #define HASH_NULL \
 	L"0000000000000000000000000000000000000000"
 
+static bool UpdateVS2017Redists(json_t *root)
+{
+	/* ------------------------------------------ *
+	 * Initialize session                         */
+
+	const DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
+
+	HttpHandle hSession = WinHttpOpen(L"OBS Studio Updater/2.1",
+	                                  WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+	                                  WINHTTP_NO_PROXY_NAME,
+	                                  WINHTTP_NO_PROXY_BYPASS,
+	                                  0);
+	if (!hSession) {
+		Status(L"Update failed: Couldn't open obsproject.com");
+		return false;
+	}
+
+	WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
+		(LPVOID)&tlsProtocols, sizeof(tlsProtocols));
+
+	HttpHandle hConnect = WinHttpConnect(hSession, L"obsproject.com",
+			INTERNET_DEFAULT_HTTPS_PORT, 0);
+	if (!hConnect) {
+		Status(L"Update failed: Couldn't connect to obsproject.com");
+		return false;
+	}
+
+	int responseCode;
+
+	DWORD waitResult = WaitForSingleObject(cancelRequested, 0);
+	if (waitResult == WAIT_OBJECT_0) {
+		return false;
+	}
+
+	/* ------------------------------------------ *
+	 * Download redist                            */
+
+	Status(L"Downloading %s", L"Visual C++ 2017 Redistributable");
+
+	const wchar_t *file = (is32bit)
+		? L"vc2017redist_x86.exe"
+		: L"vc2017redist_x64.exe";
+
+	wstring sourceURL;
+	sourceURL += L"https://obsproject.com/downloads/";
+	sourceURL += file;
+
+	wstring destPath;
+	destPath += tempPath;
+	destPath += L"\\";
+	destPath += file;
+
+	if (!HTTPGetFile(hConnect,
+			 sourceURL.c_str(),
+			 destPath.c_str(),
+			 L"Accept-Encoding: gzip",
+			 &responseCode)) {
+
+		DeleteFile(destPath.c_str());
+		Status(L"Update failed: Could not download "
+		       L"%s (error code %d)",
+		       L"Visual C++ 2017 Redistributable",
+		       responseCode);
+		return false;
+	}
+
+	/* ------------------------------------------ *
+	 * Get expected hash                          */
+
+	json_t *redistJson = json_object_get(root, is32bit
+			? "vc2017_redist_x86"
+			: "vc2017_redist_x64");
+	if (!redistJson) {
+		Status(L"Update failed: Could not parse VC2017 redist json");
+		return false;
+	}
+
+	const char *expectedHashUTF8 = json_string_value(redistJson);
+	wchar_t expectedHashWide[BLAKE2_HASH_STR_LENGTH];
+	BYTE expectedHash[BLAKE2_HASH_LENGTH];
+
+	if (!UTF8ToWideBuf(expectedHashWide, expectedHashUTF8)) {
+		DeleteFile(destPath.c_str());
+		Status(L"Update failed: Couldn't convert Json for redist hash");
+		return false;
+	}
+
+	StringToHash(expectedHashWide, expectedHash);
+
+	wchar_t downloadHashWide[BLAKE2_HASH_STR_LENGTH];
+	BYTE downloadHash[BLAKE2_HASH_LENGTH];
+
+	/* ------------------------------------------ *
+	 * Get download hash                          */
+
+	if (!CalculateFileHash(destPath.c_str(), downloadHash)) {
+		DeleteFile(destPath.c_str());
+		Status(L"Update failed: Couldn't verify integrity of %s",
+				L"Visual C++ 2017 Redistributable");
+		return false;
+	}
+
+	/* ------------------------------------------ *
+	 * If hashes do not match, integrity failed   */
+
+	HashToString(downloadHash, downloadHashWide);
+	if (wcscmp(expectedHashWide, downloadHashWide) != 0) {
+		DeleteFile(destPath.c_str());
+		Status(L"Update failed: Couldn't verify integrity of %s",
+				L"Visual C++ 2017 Redistributable");
+	}
+
+	/* ------------------------------------------ *
+	 * If hashes match, install redist            */
+
+	wchar_t commandline[MAX_PATH + MAX_PATH];
+	StringCbPrintf(commandline, sizeof(commandline),
+			L"%s /install /quiet /norestart", destPath.c_str());
+
+	PROCESS_INFORMATION pi = {};
+	STARTUPINFO si = {};
+	si.cb = sizeof(si);
+
+	bool success = !!CreateProcessW(destPath.c_str(), commandline,
+			nullptr, nullptr, false, CREATE_NO_WINDOW,
+			nullptr, nullptr, &si, &pi);
+	if (success) {
+		Status(L"Installing %s...", L"Visual C++ 2017 Redistributable");
+
+		CloseHandle(pi.hThread);
+		WaitForSingleObject(pi.hProcess, INFINITE);
+		CloseHandle(pi.hProcess);
+	} else {
+		Status(L"Update failed: Could not execute "
+		       L"%s (error code %d)",
+		       L"Visual C++ 2017 Redistributable",
+		       (int)GetLastError());
+	}
+
+	waitResult = WaitForSingleObject(cancelRequested, 0);
+	if (waitResult == WAIT_OBJECT_0) {
+		return false;
+	}
+
+	return success;
+}
+
 static bool Update(wchar_t *cmdLine)
 {
 	/* ------------------------------------- *
@@ -1043,6 +1234,15 @@ static bool Update(wchar_t *cmdLine)
 		return true;
 	}
 
+	/* ------------------------------------- *
+	 * Check for VS2017 redistributables     */
+
+	if (!HasVS2017Redist()) {
+		if (!UpdateVS2017Redists(root)) {
+			return false;
+		}
+	}
+
 	/* ------------------------------------- *
 	 * Generate file hash json               */