From aab6b3f55fc7870ff31a0545e4c4e8e5b8baee9b Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 14 Mar 2018 19:21:45 -0700 Subject: [PATCH] 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. --- UI/win-update/updater/updater.cpp | 200 ++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index a2e92cd4e..ed1557db5 100644 --- a/UI/win-update/updater/updater.cpp +++ b/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 */